redis-objects 0.4.0 → 0.5.0
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/.gitignore +3 -0
- data/CHANGELOG.rdoc +24 -0
- data/README.rdoc +5 -4
- data/Rakefile +57 -4
- data/VERSION +1 -0
- data/lib/redis/base_object.rb +3 -1
- data/lib/redis/counter.rb +0 -1
- data/lib/redis/{hash.rb → hash_key.rb} +13 -18
- data/lib/redis/helpers/serialize.rb +2 -0
- data/lib/redis/list.rb +1 -1
- data/lib/redis/objects/counters.rb +23 -10
- data/lib/redis/objects/hashes.rb +4 -3
- data/lib/redis/objects/lists.rb +3 -2
- data/lib/redis/objects/locks.rb +5 -4
- 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.rb +20 -11
- data/lib/redis/set.rb +2 -2
- data/lib/redis/sorted_set.rb +33 -22
- data/lib/redis/value.rb +0 -2
- data/redis-objects.gemspec +78 -0
- data/spec/redis_namespace_compat_spec.rb +18 -0
- data/spec/redis_objects_instance_spec.rb +52 -59
- data/spec/redis_objects_model_spec.rb +87 -4
- data/spec/spec_helper.rb +4 -2
- metadata +43 -24
data/.gitignore
ADDED
data/CHANGELOG.rdoc
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
= Changelog for Redis::Objects
|
|
2
2
|
|
|
3
|
+
== 0.5.0 [Final] (8 November 2010)
|
|
4
|
+
|
|
5
|
+
* Incompatible change: Had to rename Redis::Hash to Redis::HashKey due to internal conflicts with Redis lib and Ruby [Nate Wiger]
|
|
6
|
+
|
|
7
|
+
* Fixed AR counter override so that Redis::Objects doesn't hide AR counters [Mattias Pfeiffer]
|
|
8
|
+
|
|
9
|
+
* Fixed delete problem with Redis::List and complex values [Esdras Mayrink]
|
|
10
|
+
|
|
11
|
+
* Fix Redis::HashKey to support complex (marshaled) types [Mattias Pfeiffer]
|
|
12
|
+
|
|
13
|
+
* Group results of SortedSet#rangebyscore and #revrangebyscore if :withscores option is passed [Szymon Nowak]
|
|
14
|
+
|
|
15
|
+
* Updated Redis DEL semantics per API change [Gabe da Silveira]
|
|
16
|
+
|
|
17
|
+
== 0.4.1 [Final] (23 August 2010)
|
|
18
|
+
|
|
19
|
+
* Fixes for Ruby 1.8 failures due to missing flatten() [Gabe da Silveira]
|
|
20
|
+
|
|
21
|
+
* Enable subclasses of classes mixing in Redis::Objects to automatically pick up objects from their superclasses [Gabe da Silveira]
|
|
22
|
+
|
|
23
|
+
* Renamed prefix() and field_key() to redis_prefix() and redis_field_key() to prevent gem conflicts [Jason Meinzer]
|
|
24
|
+
|
|
25
|
+
* Fixed a typo in delete_if and added missing test coverage [Julio Capote, Nate Wiger]
|
|
26
|
+
|
|
3
27
|
== 0.4.0 [Final] (11 August 2010)
|
|
4
28
|
|
|
5
29
|
* Full support for redis hashes via new Redis::Hash class [Julio Capote, Nate Wiger]
|
data/README.rdoc
CHANGED
|
@@ -45,7 +45,7 @@ config/initializers/redis.rb is a good place for this.)
|
|
|
45
45
|
|
|
46
46
|
require 'redis'
|
|
47
47
|
require 'redis/objects'
|
|
48
|
-
Redis::Objects.redis = Redis.new(:host => 127.0.0.1, :port => 6379)
|
|
48
|
+
Redis::Objects.redis = Redis.new(:host => '127.0.0.1', :port => 6379)
|
|
49
49
|
|
|
50
50
|
Remember you can use Redis::Objects in any Ruby code. There are *no* dependencies
|
|
51
51
|
on Rails. Standalone, Sinatra, Resque - no problem.
|
|
@@ -217,10 +217,11 @@ Complex data types are no problem with :marshal => true:
|
|
|
217
217
|
=== Hashes
|
|
218
218
|
|
|
219
219
|
Hashes work like a Ruby {Hash}[http://ruby-doc.org/core/classes/Hash.html], with
|
|
220
|
-
a few Redis-specific additions.
|
|
220
|
+
a few Redis-specific additions. (The class name is "HashKey" not just "Hash", due to
|
|
221
|
+
conflicts with the Ruby core Hash class in other gems.)
|
|
221
222
|
|
|
222
|
-
require 'redis/
|
|
223
|
-
@hash = Redis::
|
|
223
|
+
require 'redis/hash_key'
|
|
224
|
+
@hash = Redis::HashKey.new('hash_name')
|
|
224
225
|
@hash['a'] = 1
|
|
225
226
|
@hash['b'] = 2
|
|
226
227
|
@hash.each do |k,v|
|
data/Rakefile
CHANGED
|
@@ -1,7 +1,60 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'jeweler'
|
|
6
|
+
Jeweler::Tasks.new do |gem|
|
|
7
|
+
gem.name = "redis-objects"
|
|
8
|
+
gem.summary = %Q{Map Redis types directly to Ruby objects}
|
|
9
|
+
gem.description = %Q{Map Redis types directly to Ruby objects. Works with any class or ORM.}
|
|
10
|
+
gem.email = "nate@wiger.org"
|
|
11
|
+
gem.homepage = "http://github.com/nateware/redis-objects"
|
|
12
|
+
gem.authors = ["Nate Wiger"]
|
|
13
|
+
gem.add_development_dependency "bacon", ">= 0"
|
|
14
|
+
gem.requirements << 'redis, v2.1.1 or greater'
|
|
15
|
+
gem.add_dependency('redis', '>= 2.1.1') # ALSO: update spec/spec_helper.rb
|
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
|
17
|
+
end
|
|
18
|
+
Jeweler::GemcutterTasks.new
|
|
19
|
+
rescue LoadError
|
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# require 'rake/testtask'
|
|
24
|
+
# Rake::TestTask.new(:spec) do |spec|
|
|
25
|
+
# spec.libs << 'lib' << 'spec'
|
|
26
|
+
# spec.pattern = 'spec/**/*_spec.rb'
|
|
27
|
+
# spec.verbose = true
|
|
28
|
+
# end
|
|
1
29
|
|
|
2
30
|
task :test do
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
31
|
+
sh "bacon spec/*_spec.rb"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
begin
|
|
36
|
+
require 'rcov/rcovtask'
|
|
37
|
+
Rcov::RcovTask.new do |spec|
|
|
38
|
+
spec.libs << 'spec'
|
|
39
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
|
40
|
+
spec.verbose = true
|
|
6
41
|
end
|
|
7
|
-
|
|
42
|
+
rescue LoadError
|
|
43
|
+
task :rcov do
|
|
44
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
task :spec => :check_dependencies
|
|
49
|
+
|
|
50
|
+
task :default => :spec
|
|
51
|
+
|
|
52
|
+
require 'rake/rdoctask'
|
|
53
|
+
Rake::RDocTask.new do |rdoc|
|
|
54
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
|
55
|
+
|
|
56
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
57
|
+
rdoc.title = "Redis::Objects #{version}"
|
|
58
|
+
rdoc.rdoc_files.include('README*')
|
|
59
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
60
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.0
|
data/lib/redis/base_object.rb
CHANGED
|
@@ -3,8 +3,10 @@ class Redis
|
|
|
3
3
|
class BaseObject
|
|
4
4
|
def initialize(key, *args)
|
|
5
5
|
@key = key.is_a?(Array) ? key.flatten.join(':') : key
|
|
6
|
-
@options = args.last.is_a?(
|
|
6
|
+
@options = args.last.is_a?(Hash) ? args.pop : {}
|
|
7
7
|
@redis = args.first || $redis
|
|
8
8
|
end
|
|
9
|
+
|
|
10
|
+
alias :inspect :to_s # Ruby 1.9.2
|
|
9
11
|
end
|
|
10
12
|
end
|
data/lib/redis/counter.rb
CHANGED
|
@@ -2,7 +2,7 @@ class Redis
|
|
|
2
2
|
#
|
|
3
3
|
# Class representing a Redis hash.
|
|
4
4
|
#
|
|
5
|
-
class
|
|
5
|
+
class HashKey < BaseObject
|
|
6
6
|
require 'enumerator'
|
|
7
7
|
include Enumerable
|
|
8
8
|
require 'redis/helpers/core_commands'
|
|
@@ -10,16 +10,11 @@ class Redis
|
|
|
10
10
|
require 'redis/helpers/serialize'
|
|
11
11
|
include Redis::Helpers::Serialize
|
|
12
12
|
|
|
13
|
-
attr_reader :key, :redis
|
|
14
|
-
|
|
15
|
-
# Needed since Redis::Hash masks bare Hash in redis.rb
|
|
16
|
-
def self.[](*args)
|
|
17
|
-
::Hash[*args]
|
|
18
|
-
end
|
|
13
|
+
attr_reader :key, :options, :redis
|
|
19
14
|
|
|
20
15
|
# Sets a field to value
|
|
21
16
|
def []=(field, value)
|
|
22
|
-
store(field, value)
|
|
17
|
+
store(field, to_redis(value))
|
|
23
18
|
end
|
|
24
19
|
|
|
25
20
|
# Gets the value of a field
|
|
@@ -29,12 +24,12 @@ class Redis
|
|
|
29
24
|
|
|
30
25
|
# Redis: HSET
|
|
31
26
|
def store(field, value)
|
|
32
|
-
redis.hset(key, field, value)
|
|
27
|
+
redis.hset(key, field, to_redis(value))
|
|
33
28
|
end
|
|
34
29
|
|
|
35
30
|
# Redis: HGET
|
|
36
31
|
def fetch(field)
|
|
37
|
-
redis.hget(key, field)
|
|
32
|
+
from_redis redis.hget(key, field)
|
|
38
33
|
end
|
|
39
34
|
|
|
40
35
|
# Verify that a field exists. Redis: HEXISTS
|
|
@@ -57,13 +52,13 @@ class Redis
|
|
|
57
52
|
|
|
58
53
|
# Return all the values of the hash. Redis: HVALS
|
|
59
54
|
def values
|
|
60
|
-
redis.hvals(key)
|
|
55
|
+
from_redis redis.hvals(key)
|
|
61
56
|
end
|
|
62
57
|
alias_method :vals, :values
|
|
63
58
|
|
|
64
59
|
# Retrieve the entire hash. Redis: HGETALL
|
|
65
60
|
def all
|
|
66
|
-
redis.hgetall(key)
|
|
61
|
+
from_redis redis.hgetall(key)
|
|
67
62
|
end
|
|
68
63
|
alias_method :clone, :all
|
|
69
64
|
|
|
@@ -71,7 +66,7 @@ class Redis
|
|
|
71
66
|
def each(&block)
|
|
72
67
|
all.each(&block)
|
|
73
68
|
end
|
|
74
|
-
|
|
69
|
+
|
|
75
70
|
# Enumerate through each keys. Redis: HKEYS
|
|
76
71
|
def each_key(&block)
|
|
77
72
|
keys.each(&block)
|
|
@@ -102,24 +97,24 @@ class Redis
|
|
|
102
97
|
# Set keys in bulk, takes a hash of field/values {'field1' => 'val1'}. Redis: HMSET
|
|
103
98
|
def bulk_set(*args)
|
|
104
99
|
raise ArgumentError, "Argument to bulk_set must be hash of key/value pairs" unless args.last.is_a?(::Hash)
|
|
105
|
-
redis.hmset(key, *args.last.
|
|
100
|
+
redis.hmset(key, *args.last.inject([]){ |arr,kv| arr + [kv[0], to_redis(kv[1])] })
|
|
106
101
|
end
|
|
107
|
-
|
|
102
|
+
|
|
108
103
|
# Get keys in bulk, takes an array of fields as arguments. Redis: HMGET
|
|
109
104
|
def bulk_get(*fields)
|
|
110
105
|
hsh = {}
|
|
111
106
|
res = redis.hmget(key, *fields.flatten)
|
|
112
107
|
fields.each do |k|
|
|
113
|
-
hsh[k] = res.shift
|
|
108
|
+
hsh[k] = from_redis(res.shift)
|
|
114
109
|
end
|
|
115
110
|
hsh
|
|
116
111
|
end
|
|
117
|
-
|
|
112
|
+
|
|
118
113
|
# Increment value by integer at field. Redis: HINCRBY
|
|
119
114
|
def incrby(field, val = 1)
|
|
120
115
|
redis.hincrby(key, field, val).to_i
|
|
121
116
|
end
|
|
122
|
-
alias_method :incr, :incrby
|
|
117
|
+
alias_method :incr, :incrby
|
|
123
118
|
|
|
124
119
|
end
|
|
125
120
|
end
|
data/lib/redis/list.rb
CHANGED
|
@@ -73,7 +73,7 @@ class Redis
|
|
|
73
73
|
# Use .del to completely delete the entire key.
|
|
74
74
|
# Redis: LREM
|
|
75
75
|
def delete(name, count=0)
|
|
76
|
-
redis.lrem(key, count, name) # weird api
|
|
76
|
+
redis.lrem(key, count, to_redis(name)) # weird api
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
# Iterate through each member of the set. Redis::Objects mixes in Enumerable,
|
|
@@ -23,10 +23,11 @@ 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
|
+
klass_name = '::' + self.name
|
|
26
27
|
if options[:global]
|
|
27
28
|
instance_eval <<-EndMethods
|
|
28
29
|
def #{name}
|
|
29
|
-
@#{name} ||= Redis::Counter.new(
|
|
30
|
+
@#{name} ||= Redis::Counter.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
30
31
|
end
|
|
31
32
|
EndMethods
|
|
32
33
|
class_eval <<-EndMethods
|
|
@@ -37,7 +38,7 @@ class Redis
|
|
|
37
38
|
else
|
|
38
39
|
class_eval <<-EndMethods
|
|
39
40
|
def #{name}
|
|
40
|
-
@#{name} ||= Redis::Counter.new(
|
|
41
|
+
@#{name} ||= Redis::Counter.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
41
42
|
end
|
|
42
43
|
EndMethods
|
|
43
44
|
end
|
|
@@ -48,7 +49,7 @@ class Redis
|
|
|
48
49
|
def get_counter(name, id=nil)
|
|
49
50
|
verify_counter_defined!(name, id)
|
|
50
51
|
initialize_counter!(name, id)
|
|
51
|
-
redis.get(
|
|
52
|
+
redis.get(redis_field_key(name, id)).to_i
|
|
52
53
|
end
|
|
53
54
|
|
|
54
55
|
# Increment a counter with the specified name and id. Accepts a block
|
|
@@ -56,7 +57,7 @@ class Redis
|
|
|
56
57
|
def increment_counter(name, id=nil, by=1, &block)
|
|
57
58
|
verify_counter_defined!(name, id)
|
|
58
59
|
initialize_counter!(name, id)
|
|
59
|
-
value = redis.incrby(
|
|
60
|
+
value = redis.incrby(redis_field_key(name, id), by).to_i
|
|
60
61
|
block_given? ? rewindable_block(:decrement_counter, name, id, value, &block) : value
|
|
61
62
|
end
|
|
62
63
|
|
|
@@ -65,7 +66,7 @@ class Redis
|
|
|
65
66
|
def decrement_counter(name, id=nil, by=1, &block)
|
|
66
67
|
verify_counter_defined!(name, id)
|
|
67
68
|
initialize_counter!(name, id)
|
|
68
|
-
value = redis.decrby(
|
|
69
|
+
value = redis.decrby(redis_field_key(name, id), by).to_i
|
|
69
70
|
block_given? ? rewindable_block(:increment_counter, name, id, value, &block) : value
|
|
70
71
|
end
|
|
71
72
|
|
|
@@ -73,21 +74,25 @@ class Redis
|
|
|
73
74
|
def reset_counter(name, id=nil, to=nil)
|
|
74
75
|
verify_counter_defined!(name, id)
|
|
75
76
|
to = @redis_objects[name][:start] if to.nil?
|
|
76
|
-
redis.set(
|
|
77
|
+
redis.set(redis_field_key(name, id), to.to_i)
|
|
77
78
|
true
|
|
78
79
|
end
|
|
79
80
|
|
|
80
81
|
private
|
|
81
82
|
|
|
82
83
|
def verify_counter_defined!(name, id) #:nodoc:
|
|
83
|
-
raise Redis::Objects::UndefinedCounter, "Undefined counter :#{name} for class #{self.name}" unless
|
|
84
|
+
raise Redis::Objects::UndefinedCounter, "Undefined counter :#{name} for class #{self.name}" unless counter_defined?(name)
|
|
84
85
|
if id.nil? and !@redis_objects[name][:global]
|
|
85
86
|
raise Redis::Objects::MissingID, "Missing ID for non-global counter #{self.name}##{name}"
|
|
86
87
|
end
|
|
87
88
|
end
|
|
88
89
|
|
|
90
|
+
def counter_defined?(name) #:nodoc:
|
|
91
|
+
@redis_objects && @redis_objects.has_key?(name)
|
|
92
|
+
end
|
|
93
|
+
|
|
89
94
|
def initialize_counter!(name, id) #:nodoc:
|
|
90
|
-
key =
|
|
95
|
+
key = redis_field_key(name, id)
|
|
91
96
|
unless @initialized_counters[key]
|
|
92
97
|
redis.setnx(key, @redis_objects[name][:start])
|
|
93
98
|
end
|
|
@@ -116,14 +121,22 @@ class Redis
|
|
|
116
121
|
# It is more efficient to use increment_[counter_name] directly.
|
|
117
122
|
# This is mainly just for completeness to override ActiveRecord.
|
|
118
123
|
def increment(name, by=1)
|
|
119
|
-
send(name)
|
|
124
|
+
if self.class.send("counter_defined?", name)
|
|
125
|
+
send(name).increment(by)
|
|
126
|
+
else
|
|
127
|
+
super
|
|
128
|
+
end
|
|
120
129
|
end
|
|
121
130
|
|
|
122
131
|
# Decrement a counter.
|
|
123
132
|
# It is more efficient to use increment_[counter_name] directly.
|
|
124
133
|
# This is mainly just for completeness to override ActiveRecord.
|
|
125
134
|
def decrement(name, by=1)
|
|
126
|
-
send(name)
|
|
135
|
+
if self.class.send("counter_defined?", name)
|
|
136
|
+
send(name).decrement(by)
|
|
137
|
+
else
|
|
138
|
+
super
|
|
139
|
+
end
|
|
127
140
|
end
|
|
128
141
|
end
|
|
129
142
|
end
|
data/lib/redis/objects/hashes.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This is the class loader, for use as "include Redis::Objects::Hashes"
|
|
2
2
|
# For the object itself, see "Redis::Hash"
|
|
3
|
-
require 'redis/
|
|
3
|
+
require 'redis/hash_key'
|
|
4
4
|
class Redis
|
|
5
5
|
module Objects
|
|
6
6
|
module Hashes
|
|
@@ -15,10 +15,11 @@ 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
|
+
klass_name = '::' + self.name
|
|
18
19
|
if options[:global]
|
|
19
20
|
instance_eval <<-EndMethods
|
|
20
21
|
def #{name}
|
|
21
|
-
@#{name} ||= Redis::
|
|
22
|
+
@#{name} ||= Redis::HashKey.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
22
23
|
end
|
|
23
24
|
EndMethods
|
|
24
25
|
class_eval <<-EndMethods
|
|
@@ -29,7 +30,7 @@ class Redis
|
|
|
29
30
|
else
|
|
30
31
|
class_eval <<-EndMethods
|
|
31
32
|
def #{name}
|
|
32
|
-
@#{name} ||= Redis::
|
|
33
|
+
@#{name} ||= Redis::HashKey.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
33
34
|
end
|
|
34
35
|
EndMethods
|
|
35
36
|
end
|
data/lib/redis/objects/lists.rb
CHANGED
|
@@ -15,10 +15,11 @@ 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
|
+
klass_name = '::' + self.name
|
|
18
19
|
if options[:global]
|
|
19
20
|
instance_eval <<-EndMethods
|
|
20
21
|
def #{name}
|
|
21
|
-
@#{name} ||= Redis::List.new(
|
|
22
|
+
@#{name} ||= Redis::List.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
22
23
|
end
|
|
23
24
|
EndMethods
|
|
24
25
|
class_eval <<-EndMethods
|
|
@@ -29,7 +30,7 @@ class Redis
|
|
|
29
30
|
else
|
|
30
31
|
class_eval <<-EndMethods
|
|
31
32
|
def #{name}
|
|
32
|
-
@#{name} ||= Redis::List.new(
|
|
33
|
+
@#{name} ||= Redis::List.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
33
34
|
end
|
|
34
35
|
EndMethods
|
|
35
36
|
end
|
data/lib/redis/objects/locks.rb
CHANGED
|
@@ -18,10 +18,11 @@ 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
|
+
klass_name = '::' + self.name
|
|
21
22
|
if options[:global]
|
|
22
23
|
instance_eval <<-EndMethods
|
|
23
24
|
def #{lock_name}(&block)
|
|
24
|
-
@#{lock_name} ||= Redis::Lock.new(
|
|
25
|
+
@#{lock_name} ||= Redis::Lock.new(redis_field_key(:#{lock_name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{lock_name}])
|
|
25
26
|
end
|
|
26
27
|
EndMethods
|
|
27
28
|
class_eval <<-EndMethods
|
|
@@ -32,7 +33,7 @@ class Redis
|
|
|
32
33
|
else
|
|
33
34
|
class_eval <<-EndMethods
|
|
34
35
|
def #{lock_name}(&block)
|
|
35
|
-
@#{lock_name} ||= Redis::Lock.new(
|
|
36
|
+
@#{lock_name} ||= Redis::Lock.new(redis_field_key(:#{lock_name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{lock_name}])
|
|
36
37
|
end
|
|
37
38
|
EndMethods
|
|
38
39
|
end
|
|
@@ -45,14 +46,14 @@ class Redis
|
|
|
45
46
|
verify_lock_defined!(name)
|
|
46
47
|
raise ArgumentError, "Missing block to #{self.name}.obtain_lock" unless block_given?
|
|
47
48
|
lock_name = "#{name}_lock"
|
|
48
|
-
Redis::Lock.new(
|
|
49
|
+
Redis::Lock.new(redis_field_key(lock_name, id), redis, @redis_objects[lock_name.to_sym]).lock(&block)
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
# Clear the lock. Use with care - usually only in an Admin page to clear
|
|
52
53
|
# stale locks (a stale lock should only happen if a server crashes.)
|
|
53
54
|
def clear_lock(name, id)
|
|
54
55
|
verify_lock_defined!(name)
|
|
55
|
-
redis.del(
|
|
56
|
+
redis.del(redis_field_key("#{name}_lock", id))
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
private
|
data/lib/redis/objects/sets.rb
CHANGED
|
@@ -15,10 +15,11 @@ 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
|
+
klass_name = '::' + self.name
|
|
18
19
|
if options[:global]
|
|
19
20
|
instance_eval <<-EndMethods
|
|
20
21
|
def #{name}
|
|
21
|
-
@#{name} ||= Redis::Set.new(
|
|
22
|
+
@#{name} ||= Redis::Set.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
22
23
|
end
|
|
23
24
|
EndMethods
|
|
24
25
|
class_eval <<-EndMethods
|
|
@@ -29,7 +30,7 @@ class Redis
|
|
|
29
30
|
else
|
|
30
31
|
class_eval <<-EndMethods
|
|
31
32
|
def #{name}
|
|
32
|
-
@#{name} ||= Redis::Set.new(
|
|
33
|
+
@#{name} ||= Redis::Set.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
33
34
|
end
|
|
34
35
|
EndMethods
|
|
35
36
|
end
|
|
@@ -15,10 +15,11 @@ 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
|
+
klass_name = '::' + self.name
|
|
18
19
|
if options[:global]
|
|
19
20
|
instance_eval <<-EndMethods
|
|
20
21
|
def #{name}
|
|
21
|
-
@#{name} ||= Redis::SortedSet.new(
|
|
22
|
+
@#{name} ||= Redis::SortedSet.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
22
23
|
end
|
|
23
24
|
EndMethods
|
|
24
25
|
class_eval <<-EndMethods
|
|
@@ -29,7 +30,7 @@ class Redis
|
|
|
29
30
|
else
|
|
30
31
|
class_eval <<-EndMethods
|
|
31
32
|
def #{name}
|
|
32
|
-
@#{name} ||= Redis::SortedSet.new(
|
|
33
|
+
@#{name} ||= Redis::SortedSet.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
33
34
|
end
|
|
34
35
|
EndMethods
|
|
35
36
|
end
|
data/lib/redis/objects/values.rb
CHANGED
|
@@ -15,10 +15,11 @@ 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
|
+
klass_name = '::' + self.name
|
|
18
19
|
if options[:global]
|
|
19
20
|
instance_eval <<-EndMethods
|
|
20
21
|
def #{name}
|
|
21
|
-
@#{name} ||= Redis::Value.new(
|
|
22
|
+
@#{name} ||= Redis::Value.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
22
23
|
end
|
|
23
24
|
def #{name}=(value)
|
|
24
25
|
#{name}.value = value
|
|
@@ -35,7 +36,7 @@ class Redis
|
|
|
35
36
|
else
|
|
36
37
|
class_eval <<-EndMethods
|
|
37
38
|
def #{name}
|
|
38
|
-
@#{name} ||= Redis::Value.new(
|
|
39
|
+
@#{name} ||= Redis::Value.new(redis_field_key(:#{name}), #{klass_name}.redis, #{klass_name}.redis_objects[:#{name}])
|
|
39
40
|
end
|
|
40
41
|
def #{name}=(value)
|
|
41
42
|
#{name}.value = value
|
data/lib/redis/objects.rb
CHANGED
|
@@ -76,32 +76,41 @@ class Redis
|
|
|
76
76
|
module ClassMethods
|
|
77
77
|
attr_accessor :redis, :redis_objects
|
|
78
78
|
|
|
79
|
-
# Set the Redis
|
|
80
|
-
def
|
|
81
|
-
def
|
|
82
|
-
@
|
|
79
|
+
# Set the Redis redis_prefix to use. Defaults to model_name
|
|
80
|
+
def redis_prefix=(redis_prefix) @redis_prefix = redis_prefix end
|
|
81
|
+
def redis_prefix(klass = self) #:nodoc:
|
|
82
|
+
@redis_prefix ||= klass.name.to_s.
|
|
83
83
|
sub(%r{(.*::)}, '').
|
|
84
84
|
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
|
85
85
|
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
|
86
86
|
downcase
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
-
def
|
|
89
|
+
def redis_field_key(name, id='') #:nodoc:
|
|
90
|
+
klass = first_ancestor_with(name)
|
|
90
91
|
# This can never ever ever ever change or upgrades will corrupt all data
|
|
91
|
-
|
|
92
|
+
klass.redis_objects[name.to_sym][:key] || "#{redis_prefix(klass)}:#{id}:#{name}"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def first_ancestor_with(name)
|
|
96
|
+
if redis_objects && redis_objects.key?(name.to_sym)
|
|
97
|
+
self
|
|
98
|
+
elsif superclass && superclass.respond_to?(:redis_objects)
|
|
99
|
+
superclass.first_ancestor_with(name)
|
|
100
|
+
end
|
|
92
101
|
end
|
|
93
102
|
end
|
|
94
103
|
|
|
95
104
|
# Instance methods that appear in your class when you include Redis::Objects.
|
|
96
105
|
module InstanceMethods
|
|
97
106
|
def redis() self.class.redis end
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
if key =
|
|
107
|
+
def redis_field_key(name) #:nodoc:
|
|
108
|
+
klass = self.class.first_ancestor_with(name)
|
|
109
|
+
if key = klass.redis_objects[name.to_sym][:key]
|
|
101
110
|
eval "%(#{key})"
|
|
102
111
|
else
|
|
103
|
-
# don't try to refactor into class
|
|
104
|
-
"#{
|
|
112
|
+
# don't try to refactor into class redis_field_key because fucks up eval context
|
|
113
|
+
"#{klass.redis_prefix}:#{id}:#{name}"
|
|
105
114
|
end
|
|
106
115
|
end
|
|
107
116
|
end
|
data/lib/redis/set.rb
CHANGED
|
@@ -45,10 +45,10 @@ class Redis
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
# Delete if matches block
|
|
48
|
-
def delete_if(&
|
|
48
|
+
def delete_if(&block)
|
|
49
49
|
res = false
|
|
50
50
|
redis.smembers(key).each do |m|
|
|
51
|
-
if
|
|
51
|
+
if block.call(from_redis(m))
|
|
52
52
|
res = redis.srem(key, m)
|
|
53
53
|
end
|
|
54
54
|
end
|
data/lib/redis/sorted_set.rb
CHANGED
|
@@ -16,7 +16,7 @@ class Redis
|
|
|
16
16
|
|
|
17
17
|
# How to add values using a sorted set. The key is the member, eg,
|
|
18
18
|
# "Peter", and the value is the score, eg, 163. So:
|
|
19
|
-
# num_posts['Peter'] = 163
|
|
19
|
+
# num_posts['Peter'] = 163
|
|
20
20
|
def []=(member, score)
|
|
21
21
|
add(member, score)
|
|
22
22
|
end
|
|
@@ -77,11 +77,7 @@ class Redis
|
|
|
77
77
|
def range(start_index, end_index, options={})
|
|
78
78
|
if options[:withscores] || options[:with_scores]
|
|
79
79
|
val = from_redis redis.zrange(key, start_index, end_index, :with_scores => true)
|
|
80
|
-
|
|
81
|
-
while k = val.shift and v = val.shift
|
|
82
|
-
ret << [k, v.to_f]
|
|
83
|
-
end
|
|
84
|
-
ret
|
|
80
|
+
group_set_with_scores(val)
|
|
85
81
|
else
|
|
86
82
|
from_redis redis.zrange(key, start_index, end_index)
|
|
87
83
|
end
|
|
@@ -91,11 +87,7 @@ class Redis
|
|
|
91
87
|
def revrange(start_index, end_index, options={})
|
|
92
88
|
if options[:withscores] || options[:with_scores]
|
|
93
89
|
val = from_redis redis.zrevrange(key, start_index, end_index, :with_scores => true)
|
|
94
|
-
|
|
95
|
-
while k = val.shift and v = val.shift
|
|
96
|
-
ret << [k, v.to_f]
|
|
97
|
-
end
|
|
98
|
-
ret
|
|
90
|
+
group_set_with_scores(val)
|
|
99
91
|
else
|
|
100
92
|
from_redis redis.zrevrange(key, start_index, end_index)
|
|
101
93
|
end
|
|
@@ -111,7 +103,13 @@ class Redis
|
|
|
111
103
|
args[:limit] = [options[:offset] || 0, options[:limit] || options[:count]] if
|
|
112
104
|
options[:offset] || options[:limit] || options[:count]
|
|
113
105
|
args[:with_scores] = true if options[:withscores] || options[:with_scores]
|
|
114
|
-
|
|
106
|
+
|
|
107
|
+
if args[:with_scores]
|
|
108
|
+
val = from_redis redis.zrangebyscore(key, min, max, args)
|
|
109
|
+
group_set_with_scores(val)
|
|
110
|
+
else
|
|
111
|
+
from_redis redis.zrangebyscore(key, min, max, args)
|
|
112
|
+
end
|
|
115
113
|
end
|
|
116
114
|
|
|
117
115
|
# Forwards compat (not yet implemented in Redis)
|
|
@@ -120,13 +118,19 @@ class Redis
|
|
|
120
118
|
args[:limit] = [options[:offset] || 0, options[:limit] || options[:count]] if
|
|
121
119
|
options[:offset] || options[:limit] || options[:count]
|
|
122
120
|
args[:with_scores] = true if options[:withscores] || options[:with_scores]
|
|
123
|
-
|
|
121
|
+
|
|
122
|
+
if args[:with_scores]
|
|
123
|
+
val = from_redis redis.zrevrangebyscore(key, min, max, args)
|
|
124
|
+
group_set_with_scores(val)
|
|
125
|
+
else
|
|
126
|
+
from_redis redis.zrevrangebyscore(key, min, max, args)
|
|
127
|
+
end
|
|
124
128
|
end
|
|
125
129
|
|
|
126
130
|
# Remove all elements in the sorted set at key with rank between start and end. Start and end are
|
|
127
131
|
# 0-based with rank 0 being the element with the lowest score. Both start and end can be negative
|
|
128
132
|
# numbers, where they indicate offsets starting at the element with the highest rank. For example:
|
|
129
|
-
# -1 is the element with the highest score, -2 the element with the second highest score and so forth.
|
|
133
|
+
# -1 is the element with the highest score, -2 the element with the second highest score and so forth.
|
|
130
134
|
# Redis: ZREMRANGEBYRANK
|
|
131
135
|
def remrangebyrank(min, max)
|
|
132
136
|
redis.zremrangebyrank(key, min, max)
|
|
@@ -144,10 +148,10 @@ class Redis
|
|
|
144
148
|
end
|
|
145
149
|
|
|
146
150
|
# Delete element if it matches block
|
|
147
|
-
def delete_if(&
|
|
151
|
+
def delete_if(&block)
|
|
148
152
|
raise ArgumentError, "Missing block to SortedSet#delete_if" unless block_given?
|
|
149
153
|
res = false
|
|
150
|
-
redis.
|
|
154
|
+
redis.zrange(key, 0, -1).each do |m|
|
|
151
155
|
if block.call(from_redis(m))
|
|
152
156
|
res = redis.zrem(key, m)
|
|
153
157
|
end
|
|
@@ -163,7 +167,7 @@ class Redis
|
|
|
163
167
|
alias_method :incr, :increment
|
|
164
168
|
alias_method :incrby, :increment
|
|
165
169
|
|
|
166
|
-
# Convenience to calling increment() with a negative number.
|
|
170
|
+
# Convenience to calling increment() with a negative number.
|
|
167
171
|
def decrement(by=1)
|
|
168
172
|
redis.zincrby(key, -by).to_i
|
|
169
173
|
end
|
|
@@ -187,7 +191,7 @@ class Redis
|
|
|
187
191
|
alias_method :intersect, :intersection
|
|
188
192
|
alias_method :inter, :intersection
|
|
189
193
|
alias_method :&, :intersection
|
|
190
|
-
|
|
194
|
+
|
|
191
195
|
# Calculate the intersection and store it in Redis as +name+. Returns the number
|
|
192
196
|
# of elements in the stored intersection. Redis: SUNIONSTORE
|
|
193
197
|
def interstore(name, *sets)
|
|
@@ -250,7 +254,7 @@ class Redis
|
|
|
250
254
|
def ==(x)
|
|
251
255
|
members == x
|
|
252
256
|
end
|
|
253
|
-
|
|
257
|
+
|
|
254
258
|
def to_s
|
|
255
259
|
members.join(', ')
|
|
256
260
|
end
|
|
@@ -270,7 +274,7 @@ class Redis
|
|
|
270
274
|
def last
|
|
271
275
|
at(-1)
|
|
272
276
|
end
|
|
273
|
-
|
|
277
|
+
|
|
274
278
|
# The number of members in the set. Aliased as size. Redis: ZCARD
|
|
275
279
|
def length
|
|
276
280
|
redis.zcard(key)
|
|
@@ -278,11 +282,18 @@ class Redis
|
|
|
278
282
|
alias_method :size, :length
|
|
279
283
|
|
|
280
284
|
private
|
|
281
|
-
|
|
285
|
+
|
|
282
286
|
def keys_from_objects(sets)
|
|
283
287
|
raise ArgumentError, "Must pass in one or more set names" if sets.empty?
|
|
284
288
|
sets.collect{|set| set.is_a?(Redis::SortedSet) ? set.key : set}
|
|
285
289
|
end
|
|
286
|
-
|
|
290
|
+
|
|
291
|
+
def group_set_with_scores(set_with_scores)
|
|
292
|
+
ret = []
|
|
293
|
+
while k = set_with_scores.shift and v = set_with_scores.shift
|
|
294
|
+
ret << [k, v.to_f]
|
|
295
|
+
end
|
|
296
|
+
ret
|
|
297
|
+
end
|
|
287
298
|
end
|
|
288
299
|
end
|
data/lib/redis/value.rb
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{redis-objects}
|
|
8
|
+
s.version = "0.5.0"
|
|
9
|
+
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
|
+
s.authors = ["Nate Wiger"]
|
|
12
|
+
s.date = %q{2010-11-08}
|
|
13
|
+
s.description = %q{Map Redis types directly to Ruby objects. Works with any class or ORM.}
|
|
14
|
+
s.email = %q{nate@wiger.org}
|
|
15
|
+
s.extra_rdoc_files = [
|
|
16
|
+
"README.rdoc"
|
|
17
|
+
]
|
|
18
|
+
s.files = [
|
|
19
|
+
".gitignore",
|
|
20
|
+
"ATOMICITY.rdoc",
|
|
21
|
+
"CHANGELOG.rdoc",
|
|
22
|
+
"README.rdoc",
|
|
23
|
+
"Rakefile",
|
|
24
|
+
"VERSION",
|
|
25
|
+
"lib/redis/base_object.rb",
|
|
26
|
+
"lib/redis/counter.rb",
|
|
27
|
+
"lib/redis/hash_key.rb",
|
|
28
|
+
"lib/redis/helpers/core_commands.rb",
|
|
29
|
+
"lib/redis/helpers/serialize.rb",
|
|
30
|
+
"lib/redis/list.rb",
|
|
31
|
+
"lib/redis/lock.rb",
|
|
32
|
+
"lib/redis/objects.rb",
|
|
33
|
+
"lib/redis/objects/counters.rb",
|
|
34
|
+
"lib/redis/objects/hashes.rb",
|
|
35
|
+
"lib/redis/objects/lists.rb",
|
|
36
|
+
"lib/redis/objects/locks.rb",
|
|
37
|
+
"lib/redis/objects/sets.rb",
|
|
38
|
+
"lib/redis/objects/sorted_sets.rb",
|
|
39
|
+
"lib/redis/objects/values.rb",
|
|
40
|
+
"lib/redis/set.rb",
|
|
41
|
+
"lib/redis/sorted_set.rb",
|
|
42
|
+
"lib/redis/value.rb",
|
|
43
|
+
"redis-objects.gemspec",
|
|
44
|
+
"spec/redis_namespace_compat_spec.rb",
|
|
45
|
+
"spec/redis_objects_instance_spec.rb",
|
|
46
|
+
"spec/redis_objects_model_spec.rb",
|
|
47
|
+
"spec/spec_helper.rb"
|
|
48
|
+
]
|
|
49
|
+
s.homepage = %q{http://github.com/nateware/redis-objects}
|
|
50
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
|
51
|
+
s.require_paths = ["lib"]
|
|
52
|
+
s.requirements = ["redis, v2.1.1 or greater"]
|
|
53
|
+
s.rubygems_version = %q{1.3.7}
|
|
54
|
+
s.summary = %q{Map Redis types directly to Ruby objects}
|
|
55
|
+
s.test_files = [
|
|
56
|
+
"spec/redis_namespace_compat_spec.rb",
|
|
57
|
+
"spec/redis_objects_instance_spec.rb",
|
|
58
|
+
"spec/redis_objects_model_spec.rb",
|
|
59
|
+
"spec/spec_helper.rb"
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
if s.respond_to? :specification_version then
|
|
63
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
64
|
+
s.specification_version = 3
|
|
65
|
+
|
|
66
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
67
|
+
s.add_development_dependency(%q<bacon>, [">= 0"])
|
|
68
|
+
s.add_runtime_dependency(%q<redis>, [">= 2.1.1"])
|
|
69
|
+
else
|
|
70
|
+
s.add_dependency(%q<bacon>, [">= 0"])
|
|
71
|
+
s.add_dependency(%q<redis>, [">= 2.1.1"])
|
|
72
|
+
end
|
|
73
|
+
else
|
|
74
|
+
s.add_dependency(%q<bacon>, [">= 0"])
|
|
75
|
+
s.add_dependency(%q<redis>, [">= 2.1.1"])
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
require 'redis/objects'
|
|
4
|
+
Redis::Objects.redis = $redis
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../../redis-namespace/lib')
|
|
7
|
+
require 'redis/namespace'
|
|
8
|
+
|
|
9
|
+
describe 'Redis::Namespace compat' do
|
|
10
|
+
it "tests the compatibility of Hash and ::Hash conflicts" do
|
|
11
|
+
ns = Redis::Namespace.new("resque", :redis => $redis)
|
|
12
|
+
ns.instance_eval { rem_namespace({"resque:x" => nil}) }.should == {"x"=>nil}
|
|
13
|
+
class Foo
|
|
14
|
+
include Redis::Objects
|
|
15
|
+
end
|
|
16
|
+
ns.instance_eval { rem_namespace({"resque:x" => nil}) }.should == {"x"=>nil}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -7,7 +7,7 @@ require 'redis/value'
|
|
|
7
7
|
require 'redis/lock'
|
|
8
8
|
require 'redis/set'
|
|
9
9
|
require 'redis/sorted_set'
|
|
10
|
-
require 'redis/
|
|
10
|
+
require 'redis/hash_key'
|
|
11
11
|
|
|
12
12
|
describe Redis::Value do
|
|
13
13
|
before do
|
|
@@ -20,7 +20,7 @@ describe Redis::Value do
|
|
|
20
20
|
@value.value = 'Trevor Hoffman'
|
|
21
21
|
@value.should == 'Trevor Hoffman'
|
|
22
22
|
@value.get.should == 'Trevor Hoffman'
|
|
23
|
-
@value.del.should
|
|
23
|
+
@value.del.should == 1
|
|
24
24
|
@value.should.be.nil
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -40,7 +40,7 @@ describe Redis::Value do
|
|
|
40
40
|
@value.value = [[1,2], {:t3 => 4}]
|
|
41
41
|
@value.should == [[1,2], {:t3 => 4}]
|
|
42
42
|
@value.get.should == [[1,2], {:t3 => 4}]
|
|
43
|
-
@value.del.should
|
|
43
|
+
@value.del.should == 1
|
|
44
44
|
@value.should.be.nil
|
|
45
45
|
@value.options[:marshal] = false
|
|
46
46
|
end
|
|
@@ -197,6 +197,9 @@ describe Redis::List do
|
|
|
197
197
|
@list << [1,2,3,[4,5]]
|
|
198
198
|
@list.last.should == [1,2,3,[4,5]]
|
|
199
199
|
@list.shift.should == {:json => 'data'}
|
|
200
|
+
@list.size.should == 2
|
|
201
|
+
@list.delete(v2)
|
|
202
|
+
@list.size.should == 1
|
|
200
203
|
@list.options[:marshal] = false
|
|
201
204
|
end
|
|
202
205
|
|
|
@@ -356,12 +359,50 @@ describe Redis::Lock do
|
|
|
356
359
|
end
|
|
357
360
|
|
|
358
361
|
|
|
359
|
-
describe Redis::
|
|
362
|
+
describe Redis::HashKey do
|
|
360
363
|
before do
|
|
361
|
-
@hash = Redis::
|
|
364
|
+
@hash = Redis::HashKey.new('test_hash')
|
|
362
365
|
@hash.clear
|
|
363
366
|
end
|
|
364
367
|
|
|
368
|
+
it "should handle complex marshaled values" do
|
|
369
|
+
@hash.options[:marshal] = true
|
|
370
|
+
@hash['abc'].should == nil
|
|
371
|
+
@hash['abc'] = {:json => 'data'}
|
|
372
|
+
@hash['abc'].should == {:json => 'data'}
|
|
373
|
+
|
|
374
|
+
# no marshaling
|
|
375
|
+
@hash.options[:marshal] = false
|
|
376
|
+
v = {:json => 'data'}
|
|
377
|
+
@hash['abc'] = v
|
|
378
|
+
@hash['abc'].should == v.to_s
|
|
379
|
+
|
|
380
|
+
@hash.options[:marshal] = true
|
|
381
|
+
@hash['abc'] = [[1,2], {:t3 => 4}]
|
|
382
|
+
@hash['abc'].should == [[1,2], {:t3 => 4}]
|
|
383
|
+
@hash.fetch('abc').should == [[1,2], {:t3 => 4}]
|
|
384
|
+
@hash.delete('abc').should == 1
|
|
385
|
+
@hash.fetch('abc').should.be.nil
|
|
386
|
+
|
|
387
|
+
@hash.options[:marshal] = true
|
|
388
|
+
@hash.bulk_set('abc' => [[1,2], {:t3 => 4}], 'def' => [[6,8], {:t4 => 8}])
|
|
389
|
+
hsh = @hash.bulk_get('abc', 'def', 'foo')
|
|
390
|
+
hsh['abc'].should == [[1,2], {:t3 => 4}]
|
|
391
|
+
hsh['def'].should == [[6,8], {:t4 => 8}]
|
|
392
|
+
hsh['foo'].should.be.nil
|
|
393
|
+
|
|
394
|
+
hsh = @hash.all
|
|
395
|
+
hsh['abc'].should == [[1,2], {:t3 => 4}]
|
|
396
|
+
hsh['def'].should == [[6,8], {:t4 => 8}]
|
|
397
|
+
|
|
398
|
+
@hash.values.should == [[[1,2], {:t3 => 4}], [[6,8], {:t4 => 8}]]
|
|
399
|
+
|
|
400
|
+
@hash.delete('def').should == 1
|
|
401
|
+
@hash.delete('abc').should == 1
|
|
402
|
+
|
|
403
|
+
@hash.options[:marshal] = false
|
|
404
|
+
end
|
|
405
|
+
|
|
365
406
|
it "should get and set values" do
|
|
366
407
|
@hash['foo'] = 'bar'
|
|
367
408
|
@hash['foo'].should == 'bar'
|
|
@@ -406,7 +447,7 @@ describe Redis::Hash do
|
|
|
406
447
|
end
|
|
407
448
|
|
|
408
449
|
it "should respond to empty?" do
|
|
409
|
-
@empty = Redis::
|
|
450
|
+
@empty = Redis::HashKey.new('test_empty_hash')
|
|
410
451
|
@empty.respond_to?(:empty?).should == true
|
|
411
452
|
end
|
|
412
453
|
|
|
@@ -501,6 +542,8 @@ describe Redis::Set do
|
|
|
501
542
|
coll = @set.select{|st| st == 'c'}
|
|
502
543
|
coll.should == ['c']
|
|
503
544
|
@set.sort.should == ['a','b','c']
|
|
545
|
+
@set.delete_if{|m| m == 'c'}
|
|
546
|
+
@set.sort.should == ['a','b']
|
|
504
547
|
end
|
|
505
548
|
|
|
506
549
|
it "should handle set intersections, unions, and diffs" do
|
|
@@ -654,61 +697,11 @@ describe Redis::SortedSet do
|
|
|
654
697
|
@set.delete('c')
|
|
655
698
|
@set.length.should == 4
|
|
656
699
|
@set.size.should == 4
|
|
700
|
+
|
|
701
|
+
@set.delete_if{|m| m == 'b'}
|
|
702
|
+
@set.size.should == 3
|
|
657
703
|
end
|
|
658
704
|
|
|
659
|
-
=begin
|
|
660
|
-
|
|
661
|
-
# Not until Redis 1.3.5 with hashes
|
|
662
|
-
it "Redis 1.3.5: should handle set intersections, unions, and diffs" do
|
|
663
|
-
@set_1['a'] = 5
|
|
664
|
-
@set_2['b'] = 18
|
|
665
|
-
@set_2['c'] = 12
|
|
666
|
-
|
|
667
|
-
@set_2['a'] = 10
|
|
668
|
-
@set_2['b'] = 15
|
|
669
|
-
@set_2['c'] = 15
|
|
670
|
-
|
|
671
|
-
(@set_1 & @set_2).sort.should == ['c','d','e']
|
|
672
|
-
|
|
673
|
-
@set_1 << 'a' << 'b' << 'c' << 'd' << 'e'
|
|
674
|
-
@set_2 << 'c' << 'd' << 'e' << 'f' << 'g'
|
|
675
|
-
@set_3 << 'a' << 'd' << 'g' << 'l' << 'm'
|
|
676
|
-
@set_1.sort.should == %w(a b c d e)
|
|
677
|
-
@set_2.sort.should == %w(c d e f g)
|
|
678
|
-
@set_3.sort.should == %w(a d g l m)
|
|
679
|
-
(@set_1 & @set_2).sort.should == ['c','d','e']
|
|
680
|
-
@set_1.intersection(@set_2).sort.should == ['c','d','e']
|
|
681
|
-
@set_1.intersection(@set_2, @set_3).sort.should == ['d']
|
|
682
|
-
@set_1.intersect(@set_2).sort.should == ['c','d','e']
|
|
683
|
-
@set_1.inter(@set_2, @set_3).sort.should == ['d']
|
|
684
|
-
@set_1.interstore(INTERSTORE_KEY, @set_2).should == 3
|
|
685
|
-
@set_1.redis.smembers(INTERSTORE_KEY).sort.should == ['c','d','e']
|
|
686
|
-
@set_1.interstore(INTERSTORE_KEY, @set_2, @set_3).should == 1
|
|
687
|
-
@set_1.redis.smembers(INTERSTORE_KEY).sort.should == ['d']
|
|
688
|
-
|
|
689
|
-
(@set_1 | @set_2).sort.should == ['a','b','c','d','e','f','g']
|
|
690
|
-
(@set_1 + @set_2).sort.should == ['a','b','c','d','e','f','g']
|
|
691
|
-
@set_1.union(@set_2).sort.should == ['a','b','c','d','e','f','g']
|
|
692
|
-
@set_1.union(@set_2, @set_3).sort.should == ['a','b','c','d','e','f','g','l','m']
|
|
693
|
-
@set_1.unionstore(UNIONSTORE_KEY, @set_2).should == 7
|
|
694
|
-
@set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g']
|
|
695
|
-
@set_1.unionstore(UNIONSTORE_KEY, @set_2, @set_3).should == 9
|
|
696
|
-
@set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g','l','m']
|
|
697
|
-
|
|
698
|
-
(@set_1 ^ @set_2).sort.should == ["a", "b"]
|
|
699
|
-
(@set_1 - @set_2).sort.should == ["a", "b"]
|
|
700
|
-
(@set_2 - @set_1).sort.should == ["f", "g"]
|
|
701
|
-
@set_1.difference(@set_2).sort.should == ["a", "b"]
|
|
702
|
-
@set_1.diff(@set_2).sort.should == ["a", "b"]
|
|
703
|
-
@set_1.difference(@set_2, @set_3).sort.should == ['b']
|
|
704
|
-
@set_1.diffstore(DIFFSTORE_KEY, @set_2).should == 2
|
|
705
|
-
@set_1.redis.smembers(DIFFSTORE_KEY).sort.should == ['a','b']
|
|
706
|
-
@set_1.diffstore(DIFFSTORE_KEY, @set_2, @set_3).should == 1
|
|
707
|
-
@set_1.redis.smembers(DIFFSTORE_KEY).sort.should == ['b']
|
|
708
|
-
end
|
|
709
|
-
|
|
710
|
-
=end
|
|
711
|
-
|
|
712
705
|
it "should support renaming sets" do
|
|
713
706
|
@set.should.be.empty
|
|
714
707
|
@set['zynga'] = 151
|
|
@@ -35,6 +35,33 @@ class Roster
|
|
|
35
35
|
def max_pitchers; 3; end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
class VanillaRoster < Roster
|
|
39
|
+
# No explicit Redis::Objects
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class CustomRoster < Roster
|
|
43
|
+
include Redis::Objects
|
|
44
|
+
|
|
45
|
+
counter :basic # Override
|
|
46
|
+
counter :special # New
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class MethodRoster
|
|
50
|
+
def increment(attribute, by=1)
|
|
51
|
+
42
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def initialize(id=1) @id = id end
|
|
55
|
+
def id; @id; end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class CustomMethodRoster < MethodRoster
|
|
59
|
+
include Redis::Objects
|
|
60
|
+
|
|
61
|
+
attr_accessor :counter
|
|
62
|
+
counter :basic
|
|
63
|
+
end
|
|
64
|
+
|
|
38
65
|
describe Redis::Objects do
|
|
39
66
|
before do
|
|
40
67
|
@roster = Roster.new
|
|
@@ -44,6 +71,9 @@ describe Redis::Objects do
|
|
|
44
71
|
@roster_2 = Roster.new(2)
|
|
45
72
|
@roster_3 = Roster.new(3)
|
|
46
73
|
|
|
74
|
+
@vanilla_roster = VanillaRoster.new
|
|
75
|
+
@custom_roster = CustomRoster.new
|
|
76
|
+
|
|
47
77
|
@roster.available_slots.reset
|
|
48
78
|
@roster.pitchers.reset
|
|
49
79
|
@roster.basic.reset
|
|
@@ -69,6 +99,9 @@ describe Redis::Objects do
|
|
|
69
99
|
@roster.all_player_stats.clear
|
|
70
100
|
@roster.total_wins.clear
|
|
71
101
|
@roster.my_rank.clear
|
|
102
|
+
|
|
103
|
+
@custom_roster.basic.reset
|
|
104
|
+
@custom_roster.special.reset
|
|
72
105
|
end
|
|
73
106
|
|
|
74
107
|
it "should provide a connection method" do
|
|
@@ -345,7 +378,7 @@ describe Redis::Objects do
|
|
|
345
378
|
@roster.starting_pitcher.get.should == 'Trevor Hoffman'
|
|
346
379
|
@roster.starting_pitcher = 'Tom Selleck'
|
|
347
380
|
@roster.starting_pitcher.should == 'Tom Selleck'
|
|
348
|
-
@roster.starting_pitcher.del.should
|
|
381
|
+
@roster.starting_pitcher.del.should == 1
|
|
349
382
|
@roster.starting_pitcher.should.be.nil
|
|
350
383
|
end
|
|
351
384
|
|
|
@@ -354,7 +387,7 @@ describe Redis::Objects do
|
|
|
354
387
|
@roster.starting_pitcher = {:json => 'data'}
|
|
355
388
|
@roster.starting_pitcher.should == {:json => 'data'}
|
|
356
389
|
@roster.starting_pitcher.get.should == {:json => 'data'}
|
|
357
|
-
@roster.starting_pitcher.del.should
|
|
390
|
+
@roster.starting_pitcher.del.should == 1
|
|
358
391
|
@roster.starting_pitcher.should.be.nil
|
|
359
392
|
end
|
|
360
393
|
|
|
@@ -612,7 +645,7 @@ describe Redis::Objects do
|
|
|
612
645
|
Roster.last_player.get.should == 'Trevor Hoffman'
|
|
613
646
|
Roster.last_player = 'Tom Selleck'
|
|
614
647
|
Roster.last_player.should == 'Tom Selleck'
|
|
615
|
-
Roster.last_player.del.should
|
|
648
|
+
Roster.last_player.del.should == 1
|
|
616
649
|
Roster.last_player.should.be.nil
|
|
617
650
|
end
|
|
618
651
|
|
|
@@ -646,7 +679,7 @@ describe Redis::Objects do
|
|
|
646
679
|
@roster2.last_player.get.should == 'Trevor Hoffman'
|
|
647
680
|
@roster2.last_player = 'Tom Selleck'
|
|
648
681
|
@roster.last_player.should == 'Tom Selleck'
|
|
649
|
-
@roster.last_player.del.should
|
|
682
|
+
@roster.last_player.del.should == 1
|
|
650
683
|
@roster.last_player.should.be.nil
|
|
651
684
|
@roster2.last_player.should.be.nil
|
|
652
685
|
end
|
|
@@ -690,4 +723,54 @@ describe Redis::Objects do
|
|
|
690
723
|
error.should.not.be.nil
|
|
691
724
|
error.should.be.kind_of(Redis::Lock::LockTimeout)
|
|
692
725
|
end
|
|
726
|
+
|
|
727
|
+
it "should pick up objects from superclass automatically" do
|
|
728
|
+
@vanilla_roster.available_slots.should.be.kind_of(Redis::Counter)
|
|
729
|
+
@vanilla_roster.pitchers.should.be.kind_of(Redis::Counter)
|
|
730
|
+
@vanilla_roster.basic.should.be.kind_of(Redis::Counter)
|
|
731
|
+
@vanilla_roster.resort_lock.should.be.kind_of(Redis::Lock)
|
|
732
|
+
@vanilla_roster.starting_pitcher.should.be.kind_of(Redis::Value)
|
|
733
|
+
@vanilla_roster.player_stats.should.be.kind_of(Redis::List)
|
|
734
|
+
@vanilla_roster.outfielders.should.be.kind_of(Redis::Set)
|
|
735
|
+
@vanilla_roster.rank.should.be.kind_of(Redis::SortedSet)
|
|
736
|
+
|
|
737
|
+
# custom keys
|
|
738
|
+
@vanilla_roster.player_totals.should.be.kind_of(Redis::Counter)
|
|
739
|
+
@vanilla_roster.all_player_stats.should.be.kind_of(Redis::List)
|
|
740
|
+
@vanilla_roster.total_wins.should.be.kind_of(Redis::Set)
|
|
741
|
+
@vanilla_roster.my_rank.should.be.kind_of(Redis::Value)
|
|
742
|
+
@vanilla_roster.weird_key.should.be.kind_of(Redis::Value)
|
|
743
|
+
|
|
744
|
+
# globals via class
|
|
745
|
+
@vanilla_roster.total_players_online.should.be.kind_of(Redis::Counter)
|
|
746
|
+
@vanilla_roster.all_player_stats.should.be.kind_of(Redis::List)
|
|
747
|
+
@vanilla_roster.all_players_online.should.be.kind_of(Redis::Set)
|
|
748
|
+
@vanilla_roster.last_player.should.be.kind_of(Redis::Value)
|
|
749
|
+
|
|
750
|
+
VanillaRoster.total_players_online.should.be.kind_of(Redis::Counter)
|
|
751
|
+
VanillaRoster.all_player_stats.should.be.kind_of(Redis::List)
|
|
752
|
+
VanillaRoster.all_players_online.should.be.kind_of(Redis::Set)
|
|
753
|
+
VanillaRoster.last_player.should.be.kind_of(Redis::Value)
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
it "should allow subclass overrides of the same redis object" do
|
|
757
|
+
@roster.basic.should == 0
|
|
758
|
+
@custom_roster.basic.increment.should == 1
|
|
759
|
+
@roster2.basic.should == 0
|
|
760
|
+
CustomRoster.new.basic.should == 1
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
it "should handle new subclass objects" do
|
|
764
|
+
@custom_roster.special.increment.should == 1
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
it "should allow passing of increment/decrement to super class" do
|
|
768
|
+
@custom_method_roster = CustomMethodRoster.new
|
|
769
|
+
@custom_method_roster.counter.should.be.nil
|
|
770
|
+
|
|
771
|
+
@custom_method_roster.increment(:counter).should == 42
|
|
772
|
+
|
|
773
|
+
@custom_method_roster.increment(:basic).should == 1
|
|
774
|
+
@custom_method_roster.basic.should.be.kind_of(Redis::Counter)
|
|
775
|
+
end
|
|
693
776
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
require 'rubygems' # poor people still on 1.8
|
|
2
|
+
gem 'redis', '>= 2.1.1'
|
|
2
3
|
require 'redis'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
|
5
6
|
require 'bacon'
|
|
6
7
|
Bacon.summary_at_exit
|
|
7
8
|
|
|
@@ -10,3 +11,4 @@ $redis = Redis.new(:host => ENV['REDIS_HOST'], :port => ENV['REDIS_PORT'])
|
|
|
10
11
|
UNIONSTORE_KEY = 'test:unionstore'
|
|
11
12
|
INTERSTORE_KEY = 'test:interstore'
|
|
12
13
|
DIFFSTORE_KEY = 'test:diffstore'
|
|
14
|
+
|
metadata
CHANGED
|
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
|
4
4
|
prerelease: false
|
|
5
5
|
segments:
|
|
6
6
|
- 0
|
|
7
|
-
-
|
|
7
|
+
- 5
|
|
8
8
|
- 0
|
|
9
|
-
version: 0.
|
|
9
|
+
version: 0.5.0
|
|
10
10
|
platform: ruby
|
|
11
11
|
authors:
|
|
12
12
|
- Nate Wiger
|
|
@@ -14,23 +14,37 @@ autorequire:
|
|
|
14
14
|
bindir: bin
|
|
15
15
|
cert_chain: []
|
|
16
16
|
|
|
17
|
-
date: 2010-08
|
|
17
|
+
date: 2010-11-08 00:00:00 -08:00
|
|
18
18
|
default_executable:
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
21
|
-
name:
|
|
21
|
+
name: bacon
|
|
22
22
|
prerelease: false
|
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
|
+
none: false
|
|
24
25
|
requirements:
|
|
25
26
|
- - ">="
|
|
26
27
|
- !ruby/object:Gem::Version
|
|
27
28
|
segments:
|
|
28
|
-
- 2
|
|
29
29
|
- 0
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
type: :runtime
|
|
30
|
+
version: "0"
|
|
31
|
+
type: :development
|
|
33
32
|
version_requirements: *id001
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: redis
|
|
35
|
+
prerelease: false
|
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
37
|
+
none: false
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
segments:
|
|
42
|
+
- 2
|
|
43
|
+
- 1
|
|
44
|
+
- 1
|
|
45
|
+
version: 2.1.1
|
|
46
|
+
type: :runtime
|
|
47
|
+
version_requirements: *id002
|
|
34
48
|
description: Map Redis types directly to Ruby objects. Works with any class or ORM.
|
|
35
49
|
email: nate@wiger.org
|
|
36
50
|
executables: []
|
|
@@ -38,18 +52,22 @@ executables: []
|
|
|
38
52
|
extensions: []
|
|
39
53
|
|
|
40
54
|
extra_rdoc_files:
|
|
55
|
+
- README.rdoc
|
|
56
|
+
files:
|
|
57
|
+
- .gitignore
|
|
41
58
|
- ATOMICITY.rdoc
|
|
42
59
|
- CHANGELOG.rdoc
|
|
43
|
-
- Rakefile
|
|
44
60
|
- README.rdoc
|
|
45
|
-
|
|
61
|
+
- Rakefile
|
|
62
|
+
- VERSION
|
|
46
63
|
- lib/redis/base_object.rb
|
|
47
64
|
- lib/redis/counter.rb
|
|
48
|
-
- lib/redis/
|
|
65
|
+
- lib/redis/hash_key.rb
|
|
49
66
|
- lib/redis/helpers/core_commands.rb
|
|
50
67
|
- lib/redis/helpers/serialize.rb
|
|
51
68
|
- lib/redis/list.rb
|
|
52
69
|
- lib/redis/lock.rb
|
|
70
|
+
- lib/redis/objects.rb
|
|
53
71
|
- lib/redis/objects/counters.rb
|
|
54
72
|
- lib/redis/objects/hashes.rb
|
|
55
73
|
- lib/redis/objects/lists.rb
|
|
@@ -57,28 +75,25 @@ files:
|
|
|
57
75
|
- lib/redis/objects/sets.rb
|
|
58
76
|
- lib/redis/objects/sorted_sets.rb
|
|
59
77
|
- lib/redis/objects/values.rb
|
|
60
|
-
- lib/redis/objects.rb
|
|
61
78
|
- lib/redis/set.rb
|
|
62
79
|
- lib/redis/sorted_set.rb
|
|
63
80
|
- lib/redis/value.rb
|
|
81
|
+
- redis-objects.gemspec
|
|
82
|
+
- spec/redis_namespace_compat_spec.rb
|
|
64
83
|
- spec/redis_objects_instance_spec.rb
|
|
65
84
|
- spec/redis_objects_model_spec.rb
|
|
66
85
|
- spec/spec_helper.rb
|
|
67
|
-
- ATOMICITY.rdoc
|
|
68
|
-
- CHANGELOG.rdoc
|
|
69
|
-
- Rakefile
|
|
70
|
-
- README.rdoc
|
|
71
86
|
has_rdoc: true
|
|
72
87
|
homepage: http://github.com/nateware/redis-objects
|
|
73
88
|
licenses: []
|
|
74
89
|
|
|
75
90
|
post_install_message:
|
|
76
91
|
rdoc_options:
|
|
77
|
-
- --
|
|
78
|
-
- Redis::Objects -- Use Redis types as Ruby objects
|
|
92
|
+
- --charset=UTF-8
|
|
79
93
|
require_paths:
|
|
80
94
|
- lib
|
|
81
95
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
96
|
+
none: false
|
|
82
97
|
requirements:
|
|
83
98
|
- - ">="
|
|
84
99
|
- !ruby/object:Gem::Version
|
|
@@ -86,6 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
86
101
|
- 0
|
|
87
102
|
version: "0"
|
|
88
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
|
+
none: false
|
|
89
105
|
requirements:
|
|
90
106
|
- - ">="
|
|
91
107
|
- !ruby/object:Gem::Version
|
|
@@ -93,11 +109,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
93
109
|
- 0
|
|
94
110
|
version: "0"
|
|
95
111
|
requirements:
|
|
96
|
-
- redis, v2.
|
|
97
|
-
rubyforge_project:
|
|
98
|
-
rubygems_version: 1.3.
|
|
112
|
+
- redis, v2.1.1 or greater
|
|
113
|
+
rubyforge_project:
|
|
114
|
+
rubygems_version: 1.3.7
|
|
99
115
|
signing_key:
|
|
100
116
|
specification_version: 3
|
|
101
|
-
summary:
|
|
102
|
-
test_files:
|
|
103
|
-
|
|
117
|
+
summary: Map Redis types directly to Ruby objects
|
|
118
|
+
test_files:
|
|
119
|
+
- spec/redis_namespace_compat_spec.rb
|
|
120
|
+
- spec/redis_objects_instance_spec.rb
|
|
121
|
+
- spec/redis_objects_model_spec.rb
|
|
122
|
+
- spec/spec_helper.rb
|