redis-objects 0.3.2 → 0.4.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/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: