redis-objects 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,8 +1,30 @@
1
1
  = Changelog for Redis::Objects
2
2
 
3
+ == 0.4.0 [Final] (11 August 2010)
4
+
5
+ * Full support for redis hashes via new Redis::Hash class [Julio Capote, Nate Wiger]
6
+
7
+ * Now dependent on redis-rb client 2.0.4 or later. Should still be backwards compatible with redis-server 1.x
8
+
9
+ * Fixes to sets and sorted sets to bring them up to speed with redis-rb 2.0 from tomstuart [Tom Stuart]
10
+
11
+ * Incompatible change: Update list[x,y] and sorted_set[x,y] to work consistently with Ruby in all cases [Tom Stuart]
12
+
13
+ * Refactoring to make constructors common across all object types from dbalatero [David Balatero]
14
+
15
+ * Renamed :withscores option to :with_scores for consistency with redis-rb 2.0, but kept backwards compat [Tom Stuart, Nate Wiger]
16
+
17
+ == 0.3.2 [Final] (21 July 2010)
18
+
19
+ * New "maxlength" option to Redis::List can create length-limited lists (eg, like a ring buffer) from dbalatero [David Balatero]
20
+
21
+ * Fix score conversions in Redis::SortedSet (scores are floats, not ints) from tomstuart [Tom Stuart]
22
+
23
+ * Switched from rspec to bacon for tests
24
+
3
25
  == 0.3.1 [Final] (1 June 2010)
4
26
 
5
- * Integrated fixes for sorted_set deletions from capotej (Julio Capote)
27
+ * Integrated fixes for sorted_set deletions from capotej [Julio Capote]
6
28
 
7
29
  == 0.3.0 [Final] (14 April 2010)
8
30
 
data/README.rdoc CHANGED
@@ -65,6 +65,8 @@ You can include Redis::Objects in any type of class:
65
65
  list :on_base
66
66
  set :outfielders
67
67
  value :at_bat
68
+ sorted_set :rank, :global => true
69
+ hash_key :pitchers_faced # "hash" is taken by Ruby
68
70
  end
69
71
 
70
72
  Familiar Ruby array operations Just Work (TM):
@@ -211,7 +213,31 @@ Complex data types are no problem with :marshal => true:
211
213
  @list.each do |el|
212
214
  puts "#{el[:name]} lives in #{el[:city]}"
213
215
  end
214
-
216
+
217
+ === Hashes
218
+
219
+ Hashes work like a Ruby {Hash}[http://ruby-doc.org/core/classes/Hash.html], with
220
+ a few Redis-specific additions.
221
+
222
+ require 'redis/hash'
223
+ @hash = Redis::Hash.new('hash_name')
224
+ @hash['a'] = 1
225
+ @hash['b'] = 2
226
+ @hash.each do |k,v|
227
+ puts "#{k} = #{v}"
228
+ end
229
+ @hash['c'] = 3
230
+ puts @hash.all # {"a"=>"1","b"=>"2","c"=>"3"}
231
+ @hash.clear
232
+
233
+ Redis also adds incrementing and bulk operations:
234
+
235
+ @hash.incr('c', 6) # 9
236
+ @hash.bulk_set('d' => 5, 'e' => 6)
237
+ @hash.bulk_get('d','e') # "5", "6"
238
+
239
+ Remember that numbers become strings in Redis. Unlike with other Redis data types, redis-objects can't guess at your data type in this situation.
240
+
215
241
  === Sets
216
242
 
217
243
  Sets work like the Ruby {Set}[http://ruby-doc.org/core/classes/Set.html] class:
@@ -221,8 +247,9 @@ Sets work like the Ruby {Set}[http://ruby-doc.org/core/classes/Set.html] class:
221
247
  @set << 'a'
222
248
  @set << 'b'
223
249
  @set << 'a' # dup ignored
224
- @set.member? 'c' # false
225
- @set.members # ['a','b']
250
+ @set.member? 'c' # false
251
+ @set.members # ['a','b']
252
+ @set.members.reverse # ['b','a']
226
253
  @set.each do |member|
227
254
  puts member
228
255
  end
@@ -293,9 +320,10 @@ a Hash and an Array. You assign like a Hash, but retrieve like an Array:
293
320
 
294
321
  @sorted_set['Newbie'] = 1
295
322
  @sorted_set.members # => ["Newbie", "Nate", "Jeff", "Peter"]
323
+ @sorted_set.members.reverse # => ["Peter", "Jeff", "Nate", "Newbie"]
296
324
 
297
325
  @sorted_set.rangebyscore(10, 100, :limit => 2) # => ["Nate", "Jeff"]
298
- @sorted_set.members(:withscores => true) # => [["Newbie", 1], ["Nate", 16], ["Jeff", 28], ["Peter", 76]]
326
+ @sorted_set.members(:with_scores => true) # => [["Newbie", 1], ["Nate", 16], ["Jeff", 28], ["Peter", 76]]
299
327
 
300
328
  # atomic increment
301
329
  @sorted_set.increment('Nate')
@@ -0,0 +1,10 @@
1
+ class Redis
2
+ # Defines base functionality for all redis-objects.
3
+ class BaseObject
4
+ def initialize(key, *args)
5
+ @key = key.is_a?(Array) ? key.flatten.join(':') : key
6
+ @options = args.last.is_a?(::Hash) ? args.pop : {} # ::Hash because of Redis::Hash
7
+ @redis = args.first || $redis
8
+ end
9
+ end
10
+ end
data/lib/redis/counter.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require File.dirname(__FILE__) + '/base_object'
2
+
1
3
  class Redis
2
4
  #
3
5
  # Class representing a Redis counter. This functions like a proxy class, in
@@ -6,15 +8,13 @@ class Redis
6
8
  # directly, or you can use the counter :foo class method in your
7
9
  # class to define a counter.
8
10
  #
9
- class Counter
11
+ class Counter < BaseObject
10
12
  require 'redis/helpers/core_commands'
11
13
  include Redis::Helpers::CoreCommands
12
14
 
13
15
  attr_reader :key, :options, :redis
14
16
  def initialize(key, *args)
15
- @key = key
16
- @options = args.last.is_a?(Hash) ? args.pop : {}
17
- @redis = args.first || $redis
17
+ super(key, *args)
18
18
  @options[:start] ||= 0
19
19
  @redis.setnx(key, @options[:start]) unless @options[:start] == 0 || @options[:init] === false
20
20
  end
data/lib/redis/hash.rb ADDED
@@ -0,0 +1,126 @@
1
+ class Redis
2
+ #
3
+ # Class representing a Redis hash.
4
+ #
5
+ class Hash < BaseObject
6
+ require 'enumerator'
7
+ include Enumerable
8
+ require 'redis/helpers/core_commands'
9
+ include Redis::Helpers::CoreCommands
10
+ require 'redis/helpers/serialize'
11
+ include Redis::Helpers::Serialize
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
19
+
20
+ # Sets a field to value
21
+ def []=(field, value)
22
+ store(field, value)
23
+ end
24
+
25
+ # Gets the value of a field
26
+ def [](field)
27
+ fetch(field)
28
+ end
29
+
30
+ # Redis: HSET
31
+ def store(field, value)
32
+ redis.hset(key, field, value)
33
+ end
34
+
35
+ # Redis: HGET
36
+ def fetch(field)
37
+ redis.hget(key, field)
38
+ end
39
+
40
+ # Verify that a field exists. Redis: HEXISTS
41
+ def has_key?(field)
42
+ redis.hexists(key, field)
43
+ end
44
+ alias_method :include?, :has_key?
45
+ alias_method :key?, :has_key?
46
+ alias_method :member?, :has_key?
47
+
48
+ # Delete field. Redis: HDEL
49
+ def delete(field)
50
+ redis.hdel(key, field)
51
+ end
52
+
53
+ # Return all the keys of the hash. Redis: HKEYS
54
+ def keys
55
+ redis.hkeys(key)
56
+ end
57
+
58
+ # Return all the values of the hash. Redis: HVALS
59
+ def values
60
+ redis.hvals(key)
61
+ end
62
+ alias_method :vals, :values
63
+
64
+ # Retrieve the entire hash. Redis: HGETALL
65
+ def all
66
+ redis.hgetall(key)
67
+ end
68
+ alias_method :clone, :all
69
+
70
+ # Enumerate through all fields. Redis: HGETALL
71
+ def each(&block)
72
+ all.each(&block)
73
+ end
74
+
75
+ # Enumerate through each keys. Redis: HKEYS
76
+ def each_key(&block)
77
+ keys.each(&block)
78
+ end
79
+
80
+ # Enumerate through all values. Redis: HVALS
81
+ def each_value(&block)
82
+ values.each(&block)
83
+ end
84
+
85
+ # Return the size of the dict. Redis: HLEN
86
+ def size
87
+ redis.hlen(key)
88
+ end
89
+ alias_method :length, :size
90
+ alias_method :count, :size
91
+
92
+ # Returns true if dict is empty
93
+ def empty?
94
+ true if size == 0
95
+ end
96
+
97
+ # Clears the dict of all keys/values. Redis: DEL
98
+ def clear
99
+ redis.del(key)
100
+ end
101
+
102
+ # Set keys in bulk, takes a hash of field/values {'field1' => 'val1'}. Redis: HMSET
103
+ def bulk_set(*args)
104
+ 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.flatten)
106
+ end
107
+
108
+ # Get keys in bulk, takes an array of fields as arguments. Redis: HMGET
109
+ def bulk_get(*fields)
110
+ hsh = {}
111
+ res = redis.hmget(key, *fields.flatten)
112
+ fields.each do |k|
113
+ hsh[k] = res.shift
114
+ end
115
+ hsh
116
+ end
117
+
118
+ # Increment value by integer at field. Redis: HINCRBY
119
+ def incrby(field, val = 1)
120
+ redis.hincrby(key, field, val).to_i
121
+ end
122
+ alias_method :incr, :incrby
123
+
124
+ end
125
+ end
126
+
data/lib/redis/list.rb CHANGED
@@ -1,9 +1,11 @@
1
+ require File.dirname(__FILE__) + '/base_object'
2
+
1
3
  class Redis
2
4
  #
3
- # Class representing a Redis list. Instances of Redis::List are designed to
5
+ # Class representing a Redis list. Instances of Redis::List are designed to
4
6
  # behave as much like Ruby arrays as possible.
5
7
  #
6
- class List
8
+ class List < BaseObject
7
9
  require 'enumerator'
8
10
  include Enumerable
9
11
  require 'redis/helpers/core_commands'
@@ -12,11 +14,6 @@ class Redis
12
14
  include Redis::Helpers::Serialize
13
15
 
14
16
  attr_reader :key, :options, :redis
15
- def initialize(key, *args)
16
- @key = key
17
- @options = args.last.is_a?(Hash) ? args.pop : {}
18
- @redis = args.first || $redis
19
- end
20
17
 
21
18
  # Works like push. Can chain together: list << 'a' << 'b'
22
19
  def <<(value)
@@ -48,7 +45,8 @@ class Redis
48
45
 
49
46
  # Return all values in the list. Redis: LRANGE(0,-1)
50
47
  def values
51
- from_redis range(0, -1)
48
+ v = from_redis range(0, -1)
49
+ v.nil? ? [] : v
52
50
  end
53
51
  alias_method :get, :values
54
52
 
@@ -59,11 +57,16 @@ class Redis
59
57
  if index.is_a? Range
60
58
  range(index.first, index.last)
61
59
  elsif length
62
- range(index, length)
60
+ case length <=> 0
61
+ when 1 then range(index, index + length - 1)
62
+ when 0 then []
63
+ when -1 then nil # Ruby does this (a bit weird)
64
+ end
63
65
  else
64
66
  at(index)
65
67
  end
66
68
  end
69
+ alias_method :slice, :[]
67
70
 
68
71
  # Delete the element(s) from the list that match name. If count is specified,
69
72
  # only the first-N (if positive) or last-N (if negative) will be removed.
data/lib/redis/lock.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require File.dirname(__FILE__) + '/base_object'
2
+
1
3
  class Redis
2
4
  #
3
5
  # Class representing a lock. This functions like a proxy class, in
@@ -6,14 +8,12 @@ class Redis
6
8
  # directly, but it is better to use the lock :foo class method in your
7
9
  # class to define a lock.
8
10
  #
9
- class Lock
11
+ class Lock < BaseObject
10
12
  class LockTimeout < StandardError; end #:nodoc:
11
13
 
12
14
  attr_reader :key, :options, :redis
13
15
  def initialize(key, *args)
14
- @key = key
15
- @options = args.last.is_a?(Hash) ? args.pop : {}
16
- @redis = args.first || $redis
16
+ super(key, *args)
17
17
  @options[:timeout] ||= 5
18
18
  @options[:init] = false if @options[:init].nil? # default :init to false
19
19
  @redis.setnx(key, @options[:start]) unless @options[:start] == 0 || @options[:init] === false
@@ -81,4 +81,4 @@ class Redis
81
81
  @options[:expiration].nil? ? 1 : (Time.now + @options[:expiration].to_f + 1).to_f
82
82
  end
83
83
  end
84
- end
84
+ end
@@ -0,0 +1,46 @@
1
+ # This is the class loader, for use as "include Redis::Objects::Hashes"
2
+ # For the object itself, see "Redis::Hash"
3
+ require 'redis/hash'
4
+ class Redis
5
+ module Objects
6
+ module Hashes
7
+ def self.included(klass)
8
+ klass.send :include, InstanceMethods
9
+ klass.extend ClassMethods
10
+ end
11
+
12
+ # Class methods that appear in your class when you include Redis::Objects.
13
+ module ClassMethods
14
+ # Define a new hash key. It will function like a regular instance
15
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
16
+ def hash_key(name, options={})
17
+ @redis_objects[name.to_sym] = options.merge(:type => :dict)
18
+ if options[:global]
19
+ instance_eval <<-EndMethods
20
+ def #{name}
21
+ @#{name} ||= Redis::Hash.new(field_key(:#{name}), redis, @redis_objects[:#{name}])
22
+ end
23
+ EndMethods
24
+ class_eval <<-EndMethods
25
+ def #{name}
26
+ self.class.#{name}
27
+ end
28
+ EndMethods
29
+ else
30
+ class_eval <<-EndMethods
31
+ def #{name}
32
+ @#{name} ||= Redis::Hash.new(field_key(:#{name}), redis, self.class.redis_objects[:#{name}])
33
+ end
34
+ EndMethods
35
+ end
36
+ end
37
+ end
38
+
39
+ # Instance methods that appear in your class when you include Redis::Objects.
40
+ module InstanceMethods
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+
data/lib/redis/objects.rb CHANGED
@@ -44,6 +44,7 @@ class Redis
44
44
  autoload :Sets, File.join(dir, 'sets')
45
45
  autoload :SortedSets, File.join(dir, 'sorted_sets')
46
46
  autoload :Values, File.join(dir, 'values')
47
+ autoload :Hashes, File.join(dir, 'hashes')
47
48
 
48
49
  class NotConnected < StandardError; end
49
50
 
@@ -67,6 +68,7 @@ class Redis
67
68
  klass.send :include, Redis::Objects::Sets
68
69
  klass.send :include, Redis::Objects::SortedSets
69
70
  klass.send :include, Redis::Objects::Values
71
+ klass.send :include, Redis::Objects::Hashes
70
72
  end
71
73
  end
72
74
 
data/lib/redis/set.rb CHANGED
@@ -1,8 +1,10 @@
1
+ require File.dirname(__FILE__) + '/base_object'
2
+
1
3
  class Redis
2
4
  #
3
5
  # Class representing a set.
4
6
  #
5
- class Set
7
+ class Set < BaseObject
6
8
  require 'enumerator'
7
9
  include Enumerable
8
10
  require 'redis/helpers/core_commands'
@@ -11,20 +13,13 @@ class Redis
11
13
  include Redis::Helpers::Serialize
12
14
 
13
15
  attr_reader :key, :options, :redis
14
-
15
- # Create a new Set.
16
- def initialize(key, *args)
17
- @key = key
18
- @options = args.last.is_a?(Hash) ? args.pop : {}
19
- @redis = args.first || $redis
20
- end
21
16
 
22
17
  # Works like add. Can chain together: list << 'a' << 'b'
23
18
  def <<(value)
24
19
  add(value)
25
20
  self # for << 'a' << 'b'
26
21
  end
27
-
22
+
28
23
  # Add the specified value to the set only if it does not exist already.
29
24
  # Redis: SADD
30
25
  def add(value)
@@ -33,7 +28,8 @@ class Redis
33
28
 
34
29
  # Return all members in the set. Redis: SMEMBERS
35
30
  def members
36
- from_redis redis.smembers(key)
31
+ v = from_redis redis.smembers(key)
32
+ v.nil? ? [] : v
37
33
  end
38
34
  alias_method :get, :members
39
35
 
@@ -156,7 +152,7 @@ class Redis
156
152
  def to_s
157
153
  members.join(', ')
158
154
  end
159
-
155
+
160
156
  private
161
157
 
162
158
  def keys_from_objects(sets)
@@ -1,8 +1,10 @@
1
+ require File.dirname(__FILE__) + '/base_object'
2
+
1
3
  class Redis
2
4
  #
3
5
  # Class representing a sorted set.
4
6
  #
5
- class SortedSet
7
+ class SortedSet < BaseObject
6
8
  # require 'enumerator'
7
9
  # include Enumerable
8
10
  require 'redis/helpers/core_commands'
@@ -11,13 +13,6 @@ class Redis
11
13
  include Redis::Helpers::Serialize
12
14
 
13
15
  attr_reader :key, :options, :redis
14
-
15
- # Create a new SortedSet.
16
- def initialize(key, *args)
17
- @key = key.is_a?(Array) ? key.flatten.join(':') : key
18
- @options = args.last.is_a?(Hash) ? args.pop : {}
19
- @redis = args.first || $redis
20
- end
21
16
 
22
17
  # How to add values using a sorted set. The key is the member, eg,
23
18
  # "Peter", and the value is the score, eg, 163. So:
@@ -40,11 +35,16 @@ class Redis
40
35
  if index.is_a? Range
41
36
  range(index.first, index.last)
42
37
  elsif length
43
- range(index, length)
38
+ case length <=> 0
39
+ when 1 then range(index, index + length - 1)
40
+ when 0 then []
41
+ when -1 then nil # Ruby does this (a bit weird)
42
+ end
44
43
  else
45
44
  score(index)
46
45
  end
47
46
  end
47
+ alias_method :slice, :[]
48
48
 
49
49
  # Return the score of the specified element of the sorted set at key. If the
50
50
  # specified element does not exist in the sorted set, or the key does not exist
@@ -68,14 +68,15 @@ class Redis
68
68
  # Return all members of the sorted set with their scores. Extremely CPU-intensive.
69
69
  # Better to use a range instead.
70
70
  def members(options={})
71
- range(0, -1, options)
71
+ v = from_redis range(0, -1, options)
72
+ v.nil? ? [] : v
72
73
  end
73
74
 
74
75
  # Return a range of values from +start_index+ to +end_index+. Can also use
75
76
  # the familiar list[start,end] Ruby syntax. Redis: ZRANGE
76
77
  def range(start_index, end_index, options={})
77
- if options[:withscores]
78
- val = from_redis redis.zrange(key, start_index, end_index, 'withscores')
78
+ if options[:withscores] || options[:with_scores]
79
+ val = from_redis redis.zrange(key, start_index, end_index, :with_scores => true)
79
80
  ret = []
80
81
  while k = val.shift and v = val.shift
81
82
  ret << [k, v.to_f]
@@ -88,8 +89,8 @@ class Redis
88
89
 
89
90
  # Return a range of values from +start_index+ to +end_index+ in reverse order. Redis: ZREVRANGE
90
91
  def revrange(start_index, end_index, options={})
91
- if options[:withscores]
92
- val = from_redis redis.zrevrange(key, start_index, end_index, 'withscores')
92
+ if options[:withscores] || options[:with_scores]
93
+ val = from_redis redis.zrevrange(key, start_index, end_index, :with_scores => true)
93
94
  ret = []
94
95
  while k = val.shift and v = val.shift
95
96
  ret << [k, v.to_f]
@@ -106,20 +107,20 @@ class Redis
106
107
  # :withscores - if true, scores are returned as well
107
108
  # Redis: ZRANGEBYSCORE
108
109
  def rangebyscore(min, max, options={})
109
- args = []
110
- args += ['limit', options[:offset] || 0, options[:limit] || options[:count]] if
110
+ args = {}
111
+ args[:limit] = [options[:offset] || 0, options[:limit] || options[:count]] if
111
112
  options[:offset] || options[:limit] || options[:count]
112
- args += ['withscores'] if options[:withscores]
113
- from_redis redis.zrangebyscore(key, min, max, *args)
113
+ args[:with_scores] = true if options[:withscores] || options[:with_scores]
114
+ from_redis redis.zrangebyscore(key, min, max, args)
114
115
  end
115
116
 
116
117
  # Forwards compat (not yet implemented in Redis)
117
118
  def revrangebyscore(min, max, options={})
118
- args = []
119
- args += ['limit', options[:offset] || 0, options[:limit] || options[:count]] if
119
+ args = {}
120
+ args[:limit] = [options[:offset] || 0, options[:limit] || options[:count]] if
120
121
  options[:offset] || options[:limit] || options[:count]
121
- args += ['withscores'] if options[:withscores]
122
- from_redis redis.zrevrangebyscore(key, min, max, *args)
122
+ args[:with_scores] = true if options[:withscores] || options[:with_scores]
123
+ from_redis redis.zrevrangebyscore(key, min, max, args)
123
124
  end
124
125
 
125
126
  # Remove all elements in the sorted set at key with rank between start and end. Start and end are
@@ -139,7 +140,19 @@ class Redis
139
140
 
140
141
  # Delete the value from the set. Redis: ZREM
141
142
  def delete(value)
142
- redis.zrem(key, value)
143
+ redis.zrem(key, to_redis(value))
144
+ end
145
+
146
+ # Delete element if it matches block
147
+ def delete_if(&blocK)
148
+ raise ArgumentError, "Missing block to SortedSet#delete_if" unless block_given?
149
+ res = false
150
+ redis.zrevrange(key, 0, -1).each do |m|
151
+ if block.call(from_redis(m))
152
+ res = redis.zrem(key, m)
153
+ end
154
+ end
155
+ res
143
156
  end
144
157
 
145
158
  # Increment the rank of that member atomically and return the new value. This
@@ -272,4 +285,4 @@ class Redis
272
285
  end
273
286
 
274
287
  end
275
- end
288
+ end
data/lib/redis/value.rb CHANGED
@@ -1,8 +1,10 @@
1
+ require File.dirname(__FILE__) + '/base_object'
2
+
1
3
  class Redis
2
4
  #
3
5
  # Class representing a simple value. You can use standard Ruby operations on it.
4
6
  #
5
- class Value
7
+ class Value < BaseObject
6
8
  require 'redis/helpers/core_commands'
7
9
  include Redis::Helpers::CoreCommands
8
10
  require 'redis/helpers/serialize'
@@ -10,9 +12,7 @@ class Redis
10
12
 
11
13
  attr_reader :key, :options, :redis
12
14
  def initialize(key, *args)
13
- @key = key
14
- @options = args.last.is_a?(Hash) ? args.pop : {}
15
- @redis = args.first || $redis
15
+ super(key, *args)
16
16
  @redis.setnx(key, @options[:default]) if @options[:default]
17
17
  end
18
18
 
@@ -32,4 +32,4 @@ class Redis
32
32
  def ==(x); value == x; end
33
33
  def nil?; value.nil?; end
34
34
  end
35
- end
35
+ end
@@ -7,6 +7,7 @@ require 'redis/value'
7
7
  require 'redis/lock'
8
8
  require 'redis/set'
9
9
  require 'redis/sorted_set'
10
+ require 'redis/hash'
10
11
 
11
12
  describe Redis::Value do
12
13
  before do
@@ -143,13 +144,22 @@ describe Redis::List do
143
144
  @list.get.should == ['a','c','f']
144
145
  @list << 'j'
145
146
  @list.should == ['a','c','f','j']
146
- @list[0..2].should == ['a','c','f']
147
- @list[0, 2].should == ['a','c','f'] # consistent with Redis, not Ruby
148
- @list[1, 3].should == ['c','f','j']
147
+ # Test against similar Ruby functionality
148
+ a = @list.values
149
+ @list[0..2].should == a[0..2]
150
+ @list.slice(0..2).should == a.slice(0..2)
151
+ @list[0, 2].should == a[0, 2]
152
+ @list.range(0, 2).should == a[0..2] # range for Redis works like .. in Ruby
153
+ @list[0, 1].should == a[0, 1]
154
+ @list.range(0, 1).should == a[0..1] # range for Redis works like .. in Ruby
155
+ @list[1, 3].should == a[1, 3]
156
+ @list.slice(1, 3).should == a.slice(1, 3)
157
+ @list[0, 0].should == []
158
+ @list[0, -1].should == a[0, -1]
149
159
  @list.length.should == 4
150
160
  @list.size.should == 4
151
- @list.should == ['a','c','f','j']
152
- @list.get.should == ['a','c','f','j']
161
+ @list.should == a
162
+ @list.get.should == a
153
163
 
154
164
  i = -1
155
165
  @list.each do |st|
@@ -345,6 +355,102 @@ describe Redis::Lock do
345
355
  end
346
356
  end
347
357
 
358
+
359
+ describe Redis::Hash do
360
+ before do
361
+ @hash = Redis::Hash.new('test_hash')
362
+ @hash.clear
363
+ end
364
+
365
+ it "should get and set values" do
366
+ @hash['foo'] = 'bar'
367
+ @hash['foo'].should == 'bar'
368
+ end
369
+
370
+ it "should know what exists" do
371
+ @hash['foo'] = 'bar'
372
+ @hash.include?('foo').should == true
373
+ end
374
+
375
+ it "should delete values" do
376
+ @hash['abc'] = 'xyz'
377
+ @hash.delete('abc')
378
+ @hash['abc'].should == nil
379
+ end
380
+
381
+ it "should respond to each" do
382
+ @hash['foo'] = 'bar'
383
+ @hash.each do |key, val|
384
+ key.should == 'foo'
385
+ val.should == 'bar'
386
+ end
387
+ end
388
+
389
+ it "should have 1 item" do
390
+ @hash['foo'] = 'bar'
391
+ @hash.size.should == 1
392
+ end
393
+
394
+ it "should respond to each_key" do
395
+ @hash['foo'] = 'bar'
396
+ @hash.each_key do |key|
397
+ key.should == 'foo'
398
+ end
399
+ end
400
+
401
+ it "should respond to each_value" do
402
+ @hash['foo'] = 'bar'
403
+ @hash.each_value do |val|
404
+ val.should == 'bar'
405
+ end
406
+ end
407
+
408
+ it "should respond to empty?" do
409
+ @empty = Redis::Hash.new('test_empty_hash')
410
+ @empty.respond_to?(:empty?).should == true
411
+ end
412
+
413
+ it "should be empty after a clear" do
414
+ @hash['foo'] = 'bar'
415
+ @hash.all.should == {'foo' => 'bar'}
416
+ @hash.clear
417
+ @hash.should.be.empty
418
+ end
419
+
420
+ it "should respond to bulk_set" do
421
+ @hash.bulk_set({'abc' => 'xyz', 'bizz' => 'bazz'})
422
+ @hash['abc'].should == 'xyz'
423
+ @hash['bizz'].should == 'bazz'
424
+
425
+ @hash.bulk_set('abc' => '123', 'bang' => 'michael')
426
+ @hash['abc'].should == '123'
427
+ @hash['bang'].should == 'michael'
428
+
429
+ @hash.bulk_set(:sym1 => 'val1', :sym2 => 'val2')
430
+ @hash['sym1'].should == 'val1'
431
+ @hash['sym2'].should == 'val2'
432
+ end
433
+
434
+ it "should respond to bulk_get" do
435
+ @hash['foo'] = 'bar'
436
+ hsh = @hash.bulk_get('abc','foo')
437
+ hsh['abc'].should == nil
438
+ hsh['foo'].should == 'bar'
439
+ end
440
+
441
+ it "should increment field" do
442
+ @hash.incr('counter')
443
+ @hash.incr('counter')
444
+ @hash['counter'].to_i.should == 2
445
+ end
446
+
447
+
448
+ after do
449
+ @hash.clear
450
+ end
451
+
452
+ end
453
+
348
454
  describe Redis::Set do
349
455
  before do
350
456
  @set = Redis::Set.new('spec/set')
@@ -366,6 +472,7 @@ describe Redis::Set do
366
472
  @set.to_s.should == 'a, b'
367
473
  @set.should == ['a','b']
368
474
  @set.members.should == ['a','b']
475
+ @set.members.reverse.should == ['b','a'] # common question
369
476
  @set.get.should == ['a','b']
370
477
  @set << 'c'
371
478
  @set.sort.should == ['a','b','c']
@@ -483,20 +590,28 @@ describe Redis::SortedSet do
483
590
  @set['b'].should == 5.6
484
591
  @set['c'] = 4
485
592
 
486
- @set[0,-1].should == ['a','c','b']
487
- @set[0..2].should == ['a','c','b']
488
- @set[0,2].should == ['a','c','b'] # consistent with Redis, not Ruby
593
+ a = @set.members
594
+ @set[0,-1].should == a[0,-1]
595
+ @set[0..2].should == a[0..2]
596
+ @set.slice(0..2).should == a.slice(0..2)
597
+ @set[0, 2].should == a[0,2]
598
+ @set.slice(0, 2).should == a.slice(0, 2)
599
+ @set.range(0, 2).should == a[0..2]
600
+ @set[0, 0].should == []
489
601
  @set.range(0,1,:withscores => true).should == [['a',3],['c',4]]
490
- @set.range(0,-1).should == ['a','c','b']
491
- @set.revrange(0,-1).should == ['b','c','a']
492
- @set[0..1].should == ['a','c']
602
+ @set.range(0,-1).should == a[0..-1]
603
+ @set.revrange(0,-1).should == a[0..-1].reverse
604
+ @set[0..1].should == a[0..1]
493
605
  @set[1].should == 0 # missing
494
606
  @set.at(1).should == 'c'
495
607
  @set.first.should == 'a'
496
608
  @set.last.should == 'b'
497
609
 
498
610
  @set.members.should == ['a','c','b']
611
+ @set.members.reverse.should == ['b','c','a']
499
612
  @set.members(:withscores => true).should == [['a',3],['c',4],['b',5.6]]
613
+ @set.members(:with_scores => true).should == [['a',3],['c',4],['b',5.6]]
614
+ @set.members(:withscores => true).reverse.should == [['b',5.6],['c',4],['a',3]]
500
615
 
501
616
  @set['b'] = 5
502
617
  @set['b'] = 6
@@ -9,10 +9,12 @@ class Roster
9
9
  counter :available_slots, :start => 10
10
10
  counter :pitchers, :limit => :max_pitchers
11
11
  counter :basic
12
+ hash_key :contact_information
12
13
  lock :resort, :timeout => 2
13
14
  value :starting_pitcher, :marshal => true
14
15
  list :player_stats, :marshal => true
15
16
  set :outfielders, :marshal => true
17
+ sorted_set :rank
16
18
 
17
19
  # global class counters
18
20
  counter :total_players_online, :global => true
@@ -49,6 +51,7 @@ describe Redis::Objects do
49
51
  @roster.starting_pitcher.delete
50
52
  @roster.player_stats.clear
51
53
  @roster.outfielders.clear
54
+ @roster.contact_information.clear
52
55
  @roster_1.outfielders.clear
53
56
  @roster_2.outfielders.clear
54
57
  @roster_3.outfielders.clear
@@ -73,21 +76,33 @@ describe Redis::Objects do
73
76
  # Roster.redis.should.be.kind_of(Redis)
74
77
  end
75
78
 
76
- it "should support custom key names" do
79
+ it "should support interpolation of key names" do
77
80
  @roster.player_totals.incr
78
81
  @roster.redis.get('players/user1/total').should == '1'
79
82
  @roster.redis.get('players/#{username}/total').should.be.nil
80
83
  @roster.all_player_stats << 'a'
81
84
  @roster.redis.lindex('players:all_stats', 0).should == 'a'
82
85
  @roster.total_wins << 'a'
83
- @roster.redis.smembers('players:1:all_stats').should == ['a']
86
+ # test for interpolation of key names
84
87
  @roster.redis.smembers('players:#{id}:all_stats').should == []
88
+ @roster.redis.smembers('players:1:all_stats').should == ['a']
85
89
  @roster.my_rank = 'a'
86
90
  @roster.redis.get('players:my_rank:user1').should == 'a'
87
91
  Roster.weird_key = 'tuka'
88
92
  Roster.redis.get('players:weird_key:#{raise}').should == 'tuka'
89
93
  end
90
94
 
95
+ it "should be able to get/set contact info" do
96
+ @roster.contact_information['John_Phone'] = '123415352'
97
+ @roster.contact_information['John_Address'] = '123 LANE'
98
+ @roster.contact_information['John_Phone'].should == '123415352'
99
+ @roster.contact_information['John_Address'].should == '123 LANE'
100
+ @roster.contact_information['asdasd'].should.be.nil
101
+ @roster.contact_information.size.should == 2
102
+ end
103
+
104
+
105
+
91
106
  it "should create counter accessors" do
92
107
  [:available_slots, :pitchers, :basic].each do |m|
93
108
  @roster.respond_to?(m).should == true
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 3
8
- - 2
9
- version: 0.3.2
7
+ - 4
8
+ - 0
9
+ version: 0.4.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Nate Wiger
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-07-21 00:00:00 -07:00
17
+ date: 2010-08-11 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -25,10 +25,10 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  segments:
28
- - 1
28
+ - 2
29
29
  - 0
30
- - 3
31
- version: 1.0.3
30
+ - 4
31
+ version: 2.0.4
32
32
  type: :runtime
33
33
  version_requirements: *id001
34
34
  description: Map Redis types directly to Ruby objects. Works with any class or ORM.
@@ -43,12 +43,15 @@ extra_rdoc_files:
43
43
  - Rakefile
44
44
  - README.rdoc
45
45
  files:
46
+ - lib/redis/base_object.rb
46
47
  - lib/redis/counter.rb
48
+ - lib/redis/hash.rb
47
49
  - lib/redis/helpers/core_commands.rb
48
50
  - lib/redis/helpers/serialize.rb
49
51
  - lib/redis/list.rb
50
52
  - lib/redis/lock.rb
51
53
  - lib/redis/objects/counters.rb
54
+ - lib/redis/objects/hashes.rb
52
55
  - lib/redis/objects/lists.rb
53
56
  - lib/redis/objects/locks.rb
54
57
  - lib/redis/objects/sets.rb
@@ -90,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
93
  - 0
91
94
  version: "0"
92
95
  requirements:
93
- - redis, v1.0.3 or greater
96
+ - redis, v2.0.4 or greater
94
97
  rubyforge_project: redis-objects
95
98
  rubygems_version: 1.3.6
96
99
  signing_key: