redis-objects 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,15 @@
1
1
  = Changelog for Redis::Objects
2
2
 
3
+ == 0.5.1 [Final] (23 May 2011)
4
+
5
+ * Fixed super class delegation conflicts with Redis Counters vs ActiveRecord [Tim Aßmann]
6
+
7
+ * Added zcount method to SortedSet [dunedain289]
8
+
9
+ * Updated redis-objects to look for Redis.current and prefer it over global $redis variable [Jean Boussier]
10
+
11
+ * Updated URLs to reflect new redis.io website [Jérémy Lecour]
12
+
3
13
  == 0.5.0 [Final] (8 November 2010)
4
14
 
5
15
  * Incompatible change: Had to rename Redis::Hash to Redis::HashKey due to internal conflicts with Redis lib and Ruby [Nate Wiger]
data/README.rdoc CHANGED
@@ -7,7 +7,7 @@ on _individual_ data structures, like counters, lists, and sets. The *atomic* p
7
7
  Using an ORM wrapper that retrieves a "record", updates values, then sends those values back,
8
8
  _removes_ the atomicity, cutting the nuts off the major advantage of Redis. Just use MySQL, k?
9
9
 
10
- This gem provides a Rubyish interface to Redis, by mapping {Redis types}[http://code.google.com/p/redis/wiki/CommandReference]
10
+ This gem provides a Rubyish interface to Redis, by mapping {Redis types}[http://redis.io/commands]
11
11
  to Ruby objects, via a thin layer over Ezra's +redis+ gem. It offers several advantages
12
12
  over the lower-level redis-rb API:
13
13
 
@@ -111,12 +111,12 @@ Finally, for free, you get a +redis+ method that points directly to a Redis conn
111
111
  @team.redis.get('somekey')
112
112
  @team.redis.smembers('someset')
113
113
 
114
- You can use the +redis+ handle to directly call any {Redis API command}[http://code.google.com/p/redis/wiki/CommandReference].
114
+ You can use the +redis+ handle to directly call any {Redis API command}[http://redis.io/commands].
115
115
 
116
116
  == Example 2: Standalone Usage
117
117
 
118
118
  There is a Ruby class that maps to each Redis type, with methods for each
119
- {Redis API command}[http://code.google.com/p/redis/wiki/CommandReference].
119
+ {Redis API command}[http://redis.io/commands].
120
120
  Note that calling +new+ does not imply it's actually a "new" value - it just
121
121
  creates a mapping between that object and the corresponding Redis data structure,
122
122
  which may already exist on the redis-server.
@@ -154,7 +154,7 @@ See the section on "Atomicity" for cool uses of atomic counter blocks.
154
154
 
155
155
  === Locks
156
156
 
157
- A convenience class that wraps the pattern of {using +setnx+ to perform locking}[http://code.google.com/p/redis/wiki/SetnxCommand].
157
+ A convenience class that wraps the pattern of {using +setnx+ to perform locking}[http://redis.io/commands/setnx].
158
158
 
159
159
  require 'redis/lock'
160
160
  @lock = Redis::Lock.new('image_resizing', :expiration => 15, :timeout => 0.1)
@@ -331,7 +331,7 @@ a Hash and an Array. You assign like a Hash, but retrieve like an Array:
331
331
  @sorted_set.incr('Peter') # shorthand
332
332
  @sorted_set.incr('Jeff', 4)
333
333
 
334
- The other Redis Sorted Set commands are supported as well; see {Sorted Sets API}[http://code.google.com/p/redis/wiki/SortedSets].
334
+ The other Redis Sorted Set commands are supported as well; see {Sorted Sets API}[http://redis.io/commands#sorted_set].
335
335
 
336
336
  == Atomic Counters and Locks
337
337
 
data/Rakefile CHANGED
@@ -27,6 +27,7 @@ end
27
27
  # spec.verbose = true
28
28
  # end
29
29
 
30
+ desc "run all the specs"
30
31
  task :test do
31
32
  sh "bacon spec/*_spec.rb"
32
33
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.5.1
@@ -4,9 +4,9 @@ class Redis
4
4
  def initialize(key, *args)
5
5
  @key = key.is_a?(Array) ? key.flatten.join(':') : key
6
6
  @options = args.last.is_a?(Hash) ? args.pop : {}
7
- @redis = args.first || $redis
7
+ @redis = args.first || $redis || Redis.current
8
8
  end
9
-
9
+
10
10
  alias :inspect :to_s # Ruby 1.9.2
11
11
  end
12
12
  end
@@ -1,3 +1,5 @@
1
+ require File.dirname(__FILE__) + '/base_object'
2
+
1
3
  class Redis
2
4
  #
3
5
  # Class representing a Redis hash.
@@ -12,6 +14,16 @@ class Redis
12
14
 
13
15
  attr_reader :key, :options, :redis
14
16
 
17
+ def initialize(key, *args)
18
+ super
19
+ @options[:marshal_keys] ||= {}
20
+ end
21
+
22
+ # Needed since Redis::Hash masks bare Hash in redis.rb
23
+ def self.[](*args)
24
+ ::Hash[*args]
25
+ end
26
+
15
27
  # Sets a field to value
16
28
  def []=(field, value)
17
29
  store(field, to_redis(value))
@@ -24,12 +36,12 @@ class Redis
24
36
 
25
37
  # Redis: HSET
26
38
  def store(field, value)
27
- redis.hset(key, field, to_redis(value))
39
+ redis.hset(key, field, to_redis(value, options[:marshal_keys][field]))
28
40
  end
29
41
 
30
42
  # Redis: HGET
31
43
  def fetch(field)
32
- from_redis redis.hget(key, field)
44
+ from_redis redis.hget(key, field), options[:marshal_keys][field]
33
45
  end
34
46
 
35
47
  # Verify that a field exists. Redis: HEXISTS
@@ -58,7 +70,9 @@ class Redis
58
70
 
59
71
  # Retrieve the entire hash. Redis: HGETALL
60
72
  def all
61
- from_redis redis.hgetall(key)
73
+ h = redis.hgetall(key)
74
+ h.each { |k,v| h[k] = from_redis(v, options[:marshal_keys][k]) }
75
+ h
62
76
  end
63
77
  alias_method :clone, :all
64
78
 
@@ -97,7 +111,9 @@ class Redis
97
111
  # Set keys in bulk, takes a hash of field/values {'field1' => 'val1'}. Redis: HMSET
98
112
  def bulk_set(*args)
99
113
  raise ArgumentError, "Argument to bulk_set must be hash of key/value pairs" unless args.last.is_a?(::Hash)
100
- redis.hmset(key, *args.last.inject([]){ |arr,kv| arr + [kv[0], to_redis(kv[1])] })
114
+ redis.hmset(key, *args.last.inject([]){ |arr,kv|
115
+ arr + [kv[0], to_redis(kv[1], options[:marshal_keys][kv[0]])]
116
+ })
101
117
  end
102
118
 
103
119
  # Get keys in bulk, takes an array of fields as arguments. Redis: HMGET
@@ -105,14 +121,19 @@ class Redis
105
121
  hsh = {}
106
122
  res = redis.hmget(key, *fields.flatten)
107
123
  fields.each do |k|
108
- hsh[k] = from_redis(res.shift)
124
+ hsh[k] = from_redis(res.shift, options[:marshal_keys][k])
109
125
  end
110
126
  hsh
111
127
  end
112
128
 
113
129
  # Increment value by integer at field. Redis: HINCRBY
114
130
  def incrby(field, val = 1)
115
- redis.hincrby(key, field, val).to_i
131
+ ret = redis.hincrby(key, field, val)
132
+ unless ret.is_a? Array
133
+ ret.to_i
134
+ else
135
+ nil
136
+ end
116
137
  end
117
138
  alias_method :incr, :incrby
118
139
 
@@ -41,14 +41,12 @@ class Redis
41
41
  def move(dbindex)
42
42
  redis.move key, dbindex
43
43
  end
44
-
45
- # See the documentation for SORT: http://code.google.com/p/redis/wiki/SortCommand
46
- # TODO
47
- # def sort(options)
48
- # args = []
49
- # args += ['sort']
50
- # from_redis redis.sort key
51
- # end
44
+
45
+ def sort(options={})
46
+ options[:order] ||= "asc alpha"
47
+ redis.sort(key, options)
48
+ end
52
49
  end
50
+
53
51
  end
54
52
  end
@@ -3,8 +3,8 @@ class Redis
3
3
  module Serialize
4
4
  include Marshal
5
5
 
6
- def to_redis(value)
7
- return value unless options[:marshal]
6
+ def to_redis(value, marshal=false)
7
+ return value unless options[:marshal] || marshal
8
8
  case value
9
9
  when String, Fixnum, Bignum, Float
10
10
  value
@@ -13,8 +13,8 @@ class Redis
13
13
  end
14
14
  end
15
15
 
16
- def from_redis(value)
17
- return value unless options[:marshal]
16
+ def from_redis(value, marshal=false)
17
+ return value unless options[:marshal] || marshal
18
18
  case value
19
19
  when Array
20
20
  value.collect{|v| from_redis(v)}
@@ -26,4 +26,4 @@ class Redis
26
26
  end
27
27
  end
28
28
  end
29
- end
29
+ end
data/lib/redis/lock.rb CHANGED
@@ -41,7 +41,7 @@ class Redis
41
41
 
42
42
  # Lock is being held. Now check to see if it's expired (if we're using
43
43
  # lock expiration).
44
- # See "Handling Deadlocks" section on http://code.google.com/p/redis/wiki/SetnxCommand
44
+ # See "Handling Deadlocks" section on http://redis.io/commands/setnx
45
45
  if !@options[:expiration].nil?
46
46
  old_expiration = redis.get(key).to_f
47
47
 
@@ -55,6 +55,7 @@ class Redis
55
55
  # Increment a counter with the specified name and id. Accepts a block
56
56
  # like the instance method. See Redis::Objects::Counter for details.
57
57
  def increment_counter(name, id=nil, by=1, &block)
58
+ return super(name, id) unless counter_defined?(name)
58
59
  verify_counter_defined!(name, id)
59
60
  initialize_counter!(name, id)
60
61
  value = redis.incrby(redis_field_key(name, id), by).to_i
@@ -64,6 +65,7 @@ class Redis
64
65
  # Decrement a counter with the specified name and id. Accepts a block
65
66
  # like the instance method. See Redis::Objects::Counter for details.
66
67
  def decrement_counter(name, id=nil, by=1, &block)
68
+ return super(name, id) unless counter_defined?(name)
67
69
  verify_counter_defined!(name, id)
68
70
  initialize_counter!(name, id)
69
71
  value = redis.decrby(redis_field_key(name, id), by).to_i
@@ -81,7 +83,7 @@ class Redis
81
83
  private
82
84
 
83
85
  def verify_counter_defined!(name, id) #:nodoc:
84
- raise Redis::Objects::UndefinedCounter, "Undefined counter :#{name} for class #{self.name}" unless counter_defined?(name)
86
+ raise NoMethodError, "Undefined counter :#{name} for class #{self.name}" unless counter_defined?(name)
85
87
  if id.nil? and !@redis_objects[name][:global]
86
88
  raise Redis::Objects::MissingID, "Missing ID for non-global counter #{self.name}##{name}"
87
89
  end
@@ -141,4 +143,4 @@ class Redis
141
143
  end
142
144
  end
143
145
  end
144
- end
146
+ end
data/lib/redis/objects.rb CHANGED
@@ -46,12 +46,13 @@ class Redis
46
46
  autoload :Values, File.join(dir, 'values')
47
47
  autoload :Hashes, File.join(dir, 'hashes')
48
48
 
49
- class NotConnected < StandardError; end
49
+ class NotConnected < StandardError; end
50
+ class NilObjectId < StandardError; end
50
51
 
51
52
  class << self
52
53
  def redis=(conn) @redis = conn end
53
54
  def redis
54
- @redis ||= $redis || raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection")
55
+ @redis ||= $redis || Redis.current || raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection")
55
56
  end
56
57
 
57
58
  def included(klass)
@@ -74,7 +75,9 @@ class Redis
74
75
 
75
76
  # Class methods that appear in your class when you include Redis::Objects.
76
77
  module ClassMethods
77
- attr_accessor :redis, :redis_objects
78
+ attr_writer :redis
79
+ attr_accessor :redis_objects
80
+ def redis() @redis ||= Objects.redis end
78
81
 
79
82
  # Set the Redis redis_prefix to use. Defaults to model_name
80
83
  def redis_prefix=(redis_prefix) @redis_prefix = redis_prefix end
@@ -86,10 +89,19 @@ class Redis
86
89
  downcase
87
90
  end
88
91
 
89
- def redis_field_key(name, id='') #:nodoc:
92
+ def redis_field_key(name, id=nil) #:nodoc:
90
93
  klass = first_ancestor_with(name)
91
- # This can never ever ever ever change or upgrades will corrupt all data
92
- klass.redis_objects[name.to_sym][:key] || "#{redis_prefix(klass)}:#{id}:#{name}"
94
+ # READ THIS: This can never ever ever ever change or upgrades will corrupt all data
95
+ # I don't think people were using Proc as keys before (that would create a weird key). Should be ok
96
+ key = klass.redis_objects[name.to_sym][:key]
97
+ if key && key.respond_to?(:call)
98
+ key = key.call self
99
+ end
100
+ if id.nil? and !klass.redis_objects[name.to_sym][:global]
101
+ raise NilObjectId,
102
+ "[#{klass.redis_objects[name.to_sym]}] Attempt to address redis-object :#{name} on class #{klass.name} with nil id (unsaved record?) [object_id=#{object_id}]"
103
+ end
104
+ key || "#{redis_prefix(klass)}:#{id}:#{name}"
93
105
  end
94
106
 
95
107
  def first_ancestor_with(name)
@@ -107,8 +119,16 @@ class Redis
107
119
  def redis_field_key(name) #:nodoc:
108
120
  klass = self.class.first_ancestor_with(name)
109
121
  if key = klass.redis_objects[name.to_sym][:key]
110
- eval "%(#{key})"
122
+ if key.respond_to?(:call)
123
+ key.call self
124
+ else
125
+ eval "%(#{key})"
126
+ end
111
127
  else
128
+ if id.nil? and !klass.redis_objects[name.to_sym][:global]
129
+ raise NilObjectId,
130
+ "Attempt to address redis-object :#{name} on class #{klass.name} with nil id (unsaved record?) [object_id=#{object_id}]"
131
+ end
112
132
  # don't try to refactor into class redis_field_key because fucks up eval context
113
133
  "#{klass.redis_prefix}:#{id}:#{name}"
114
134
  end
@@ -280,6 +280,11 @@ class Redis
280
280
  redis.zcard(key)
281
281
  end
282
282
  alias_method :size, :length
283
+
284
+ # The number of members within a range of scores. Redis: ZCOUNT
285
+ def range_size(min, max)
286
+ redis.zcount(key, min, max)
287
+ end
283
288
 
284
289
  private
285
290
 
@@ -1,66 +1,58 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{redis-objects}
8
- s.version = "0.5.0"
8
+ s.version = "0.5.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nate Wiger"]
12
- s.date = %q{2010-11-08}
12
+ s.date = %q{2011-05-23}
13
13
  s.description = %q{Map Redis types directly to Ruby objects. Works with any class or ORM.}
14
14
  s.email = %q{nate@wiger.org}
15
15
  s.extra_rdoc_files = [
16
16
  "README.rdoc"
17
17
  ]
18
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"
19
+ "ATOMICITY.rdoc",
20
+ "CHANGELOG.rdoc",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "lib/redis/base_object.rb",
25
+ "lib/redis/counter.rb",
26
+ "lib/redis/hash_key.rb",
27
+ "lib/redis/helpers/core_commands.rb",
28
+ "lib/redis/helpers/serialize.rb",
29
+ "lib/redis/list.rb",
30
+ "lib/redis/lock.rb",
31
+ "lib/redis/objects.rb",
32
+ "lib/redis/objects/counters.rb",
33
+ "lib/redis/objects/hashes.rb",
34
+ "lib/redis/objects/lists.rb",
35
+ "lib/redis/objects/locks.rb",
36
+ "lib/redis/objects/sets.rb",
37
+ "lib/redis/objects/sorted_sets.rb",
38
+ "lib/redis/objects/values.rb",
39
+ "lib/redis/set.rb",
40
+ "lib/redis/sorted_set.rb",
41
+ "lib/redis/value.rb",
42
+ "redis-objects.gemspec",
43
+ "spec/redis_namespace_compat_spec.rb",
44
+ "spec/redis_objects_active_record_spec.rb",
45
+ "spec/redis_objects_instance_spec.rb",
46
+ "spec/redis_objects_model_spec.rb",
47
+ "spec/spec_helper.rb"
48
48
  ]
49
49
  s.homepage = %q{http://github.com/nateware/redis-objects}
50
- s.rdoc_options = ["--charset=UTF-8"]
51
50
  s.require_paths = ["lib"]
52
51
  s.requirements = ["redis, v2.1.1 or greater"]
53
- s.rubygems_version = %q{1.3.7}
52
+ s.rubygems_version = %q{1.7.2}
54
53
  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
54
 
62
55
  if s.respond_to? :specification_version then
63
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
64
56
  s.specification_version = 3
65
57
 
66
58
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
@@ -4,15 +4,21 @@ require 'redis/objects'
4
4
  Redis::Objects.redis = $redis
5
5
 
6
6
  $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../../redis-namespace/lib')
7
- require 'redis/namespace'
7
+ begin
8
+ require 'redis/namespace'
8
9
 
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
10
+ describe 'Redis::Namespace compat' do
11
+ it "tests the compatibility of Hash and ::Hash conflicts" do
12
+ ns = Redis::Namespace.new("resque", :redis => $redis)
13
+ ns.instance_eval { rem_namespace({"resque:x" => nil}) }.should == {"x"=>nil}
14
+ class Foo
15
+ include Redis::Objects
16
+ end
17
+ ns.instance_eval { rem_namespace({"resque:x" => nil}) }.should == {"x"=>nil}
15
18
  end
16
- ns.instance_eval { rem_namespace({"resque:x" => nil}) }.should == {"x"=>nil}
17
19
  end
20
+
21
+ rescue LoadError
22
+ # Redis::Namespace not installed
23
+ puts "Skipping Redis::Namespace tests as redis-namespace is not installed"
18
24
  end
@@ -0,0 +1,105 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ require 'redis/objects'
5
+ Redis::Objects.redis = $redis
6
+
7
+ begin
8
+ require 'active_record'
9
+ ActiveRecord::Base.establish_connection(
10
+ :adapter => 'sqlite3',
11
+ :database => File.expand_path(File.dirname(__FILE__) + '/redis_objects_test.sqlite3')
12
+ )
13
+
14
+ class CreateBlogs < ActiveRecord::Migration
15
+ def self.up
16
+ create_table :blogs do |t|
17
+ t.string :name
18
+ t.integer :posts_count, :default => 0
19
+ t.timestamps
20
+ end
21
+ end
22
+
23
+ def self.down
24
+ drop_table :blogs
25
+ end
26
+ end
27
+
28
+ class Blog < ActiveRecord::Base
29
+ include Redis::Objects
30
+ has_many :posts
31
+ end
32
+
33
+ class CreatePosts < ActiveRecord::Migration
34
+ def self.up
35
+ create_table :posts do |t|
36
+ t.string :title
37
+ t.string :description, :length => 200
38
+ t.integer :total
39
+ t.integer :blog_id
40
+ t.timestamps
41
+ end
42
+ end
43
+
44
+ def self.down
45
+ drop_table :posts
46
+ end
47
+ end
48
+
49
+ class Post < ActiveRecord::Base
50
+ include Redis::Objects
51
+ counter :total
52
+ belongs_to :blog, :counter_cache => true
53
+ end
54
+
55
+
56
+ describe Redis::Objects do
57
+ before do
58
+ CreatePosts.up
59
+ CreateBlogs.up
60
+ end
61
+ after do
62
+ CreatePosts.down
63
+ CreateBlogs.down
64
+ end
65
+
66
+ it "exercises ActiveRecord in more detail" do
67
+ @ar = Post.new
68
+ @ar.save!
69
+ @ar.destroy
70
+
71
+ # @ar.total.reset
72
+ @ar2 = Post.new
73
+ @ar2.save!
74
+ @ar2.total.reset
75
+ @ar2.total.increment.should == 1
76
+ @ar2.id.should == 2
77
+ @ar2.increment(:total).should == 2
78
+ @ar2[:total].should == nil # DB column
79
+ @ar2.redis.get(@ar2.redis_field_key('total')).to_i.should == 2
80
+ @ar2[:total] = 3 # DB column
81
+ @ar2.total.decrement.should == 1
82
+ @ar2.total.reset
83
+ @ar2.total.should == 0
84
+ @ar2.destroy
85
+ end
86
+
87
+ it "falls back to ActiveRecord if redis counter is not defined" do
88
+ blog = Blog.create
89
+ blog.reload.posts_count.should == 0
90
+ post = Post.create :blog => blog
91
+ blog.reload.posts_count.should == 1
92
+ blog2 = Blog.create
93
+ Post.create :blog => blog2
94
+ Post.create :blog => blog2
95
+ blog.reload.posts_count.should == 1
96
+ blog2.reload.posts_count.should == 2
97
+ blog.posts_count.should == 1
98
+ end
99
+ end
100
+
101
+
102
+ rescue LoadError
103
+ # ActiveRecord not install
104
+ puts "Skipping ActiveRecord tests as active_record is not installed"
105
+ end
@@ -360,6 +360,39 @@ end
360
360
 
361
361
 
362
362
  describe Redis::HashKey do
363
+ describe "With Marshal" do
364
+ before do
365
+ @hash = Redis::HashKey.new('test_hash', $redis,
366
+ {:marshal_keys=>{'created_at'=>true}})
367
+ @hash.clear
368
+ end
369
+
370
+ it "should marshal specified keys" do
371
+ @hash['created_at'] = Time.now
372
+ @hash['created_at'].class.should == Time
373
+ end
374
+
375
+ it "should not marshal unless required" do
376
+ @hash['updated_at'] = Time.now
377
+ @hash['updated_at'].class.should == String
378
+ end
379
+
380
+ it "should marshall appropriate key with bulk set and get" do
381
+ @hash.bulk_set({'created_at'=>Time.now, 'updated_at'=>Time.now})
382
+
383
+ @hash['created_at'].class.should == Time
384
+ @hash['updated_at'].class.should == String
385
+
386
+ h = @hash.bulk_get('created_at', 'updated_at')
387
+ h['created_at'].class.should == Time
388
+ h['updated_at'].class.should == String
389
+
390
+ h = @hash.all
391
+ h['created_at'].class.should == Time
392
+ h['updated_at'].class.should == String
393
+ end
394
+ end
395
+
363
396
  before do
364
397
  @hash = Redis::HashKey.new('test_hash')
365
398
  @hash.clear
@@ -601,11 +634,40 @@ describe Redis::Set do
601
634
  @set.redis.del('spec/set2')
602
635
  end
603
636
 
637
+ it "should support sorting" do
638
+ @set_1 << 'a' << 'b' << 'c' << 'd' << 'e'
639
+ @set_2 << 1 << 2 << 3 << 4 << 5
640
+ @set_3 << 'm_1' << 'm_2'
641
+ @set_1.sort.should == %w(a b c d e)
642
+ @set_2.sort.should == %w(1 2 3 4 5)
643
+
644
+ @set_1.sort(SORT_ORDER).should == %w(e d c b a)
645
+ @set_3.sort(SORT_BY).should == %w(m_1 m_2)
646
+ @set_2.sort(SORT_LIMIT).should == %w(3 4)
647
+
648
+ val1 = Redis::Value.new('spec/3/sorted')
649
+ val2 = Redis::Value.new('spec/4/sorted')
650
+
651
+ val1.set('val3')
652
+ val2.set('val4')
653
+
654
+ @set_2.sort(SORT_GET).should == ['val3', 'val4']
655
+ @set_2.sort(SORT_STORE).should == 2
656
+ @set_2.redis.type(SORT_STORE[:store]).should == 'list'
657
+ @set_2.redis.lrange(SORT_STORE[:store], 0, -1).should == ['val3', 'val4']
658
+
659
+ @set_1.redis.del val1.key
660
+ @set_1.redis.del val2.key
661
+ @set_1.redis.del SORT_STORE[:store]
662
+
663
+ end
664
+
604
665
  after do
605
666
  @set.clear
606
667
  @set_1.clear
607
668
  @set_2.clear
608
669
  @set_3.clear
670
+
609
671
  end
610
672
  end
611
673
 
@@ -698,6 +760,10 @@ describe Redis::SortedSet do
698
760
  @set.length.should == 4
699
761
  @set.size.should == 4
700
762
 
763
+ @set.range_size(100, 120).should == 0
764
+ @set.range_size(0, 100).should == 2
765
+ @set.range_size('-inf', 'inf').should == 4
766
+
701
767
  @set.delete_if{|m| m == 'b'}
702
768
  @set.size.should == 3
703
769
  end
@@ -9,7 +9,7 @@ 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
+ hash_key :contact_information, :marshal_keys=>{'updated_at'=>true}
13
13
  lock :resort, :timeout => 2
14
14
  value :starting_pitcher, :marshal => true
15
15
  list :player_stats, :marshal => true
@@ -18,17 +18,19 @@ class Roster
18
18
 
19
19
  # global class counters
20
20
  counter :total_players_online, :global => true
21
- list :all_player_stats, :global => true
22
21
  set :all_players_online, :global => true
23
22
  value :last_player, :global => true
24
23
 
25
24
  # custom keys
26
25
  counter :player_totals, :key => 'players/#{username}/total'
27
- list :all_player_stats, :key => 'players:all_stats'
26
+ list :all_player_stats, :key => 'players:all_stats', :global => true
28
27
  set :total_wins, :key => 'players:#{id}:all_stats'
29
28
  value :my_rank, :key => 'players:my_rank:#{username}'
30
29
  value :weird_key, :key => 'players:weird_key:#{raise}', :global => true
31
30
 
31
+ #callable as key
32
+ counter :daily, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" }
33
+
32
34
  def initialize(id=1) @id = id end
33
35
  def id; @id; end
34
36
  def username; "user#{id}"; end
@@ -100,6 +102,8 @@ describe Redis::Objects do
100
102
  @roster.total_wins.clear
101
103
  @roster.my_rank.clear
102
104
 
105
+ @roster.daily.clear
106
+
103
107
  @custom_roster.basic.reset
104
108
  @custom_roster.special.reset
105
109
  end
@@ -108,7 +112,7 @@ describe Redis::Objects do
108
112
  Roster.redis.should == Redis::Objects.redis
109
113
  # Roster.redis.should.be.kind_of(Redis)
110
114
  end
111
-
115
+
112
116
  it "should support interpolation of key names" do
113
117
  @roster.player_totals.incr
114
118
  @roster.redis.get('players/user1/total').should == '1'
@@ -123,6 +127,10 @@ describe Redis::Objects do
123
127
  @roster.redis.get('players:my_rank:user1').should == 'a'
124
128
  Roster.weird_key = 'tuka'
125
129
  Roster.redis.get('players:weird_key:#{raise}').should == 'tuka'
130
+
131
+ k = "Roster:#{Time.now.strftime('%Y-%m-%dT%H')}:daily"
132
+ @roster.daily.incr
133
+ @roster.redis.get(k).should == '1'
126
134
  end
127
135
 
128
136
  it "should be able to get/set contact info" do
@@ -134,6 +142,10 @@ describe Redis::Objects do
134
142
  @roster.contact_information.size.should == 2
135
143
  end
136
144
 
145
+ it "should be marshalling hash keys" do
146
+ @roster.contact_information['updated_at'] = Time.now
147
+ @roster.contact_information['updated_at'].class.should == Time
148
+ end
137
149
 
138
150
 
139
151
  it "should create counter accessors" do
@@ -327,7 +339,7 @@ describe Redis::Objects do
327
339
  Roster.increment_counter(:badness, 2)
328
340
  rescue => error
329
341
  end
330
- error.should.be.kind_of(Redis::Objects::UndefinedCounter)
342
+ error.should.be.kind_of(NoMethodError)
331
343
 
332
344
  error = nil
333
345
  begin
@@ -758,6 +770,7 @@ describe Redis::Objects do
758
770
  @custom_roster.basic.increment.should == 1
759
771
  @roster2.basic.should == 0
760
772
  CustomRoster.new.basic.should == 1
773
+ @custom_roster.basic.decrement.should == 0
761
774
  end
762
775
 
763
776
  it "should handle new subclass objects" do
@@ -767,10 +780,15 @@ describe Redis::Objects do
767
780
  it "should allow passing of increment/decrement to super class" do
768
781
  @custom_method_roster = CustomMethodRoster.new
769
782
  @custom_method_roster.counter.should.be.nil
770
-
783
+
771
784
  @custom_method_roster.increment(:counter).should == 42
772
-
785
+
773
786
  @custom_method_roster.increment(:basic).should == 1
787
+ @custom_method_roster.basic.increment.should == 2
788
+ @custom_method_roster.decrement(:basic).should == 1
789
+ @custom_method_roster.basic.decrement.should == 0
790
+ @custom_method_roster.basic.reset.should.be.true
791
+ @custom_method_roster.basic.should == 0
774
792
  @custom_method_roster.basic.should.be.kind_of(Redis::Counter)
775
793
  end
776
794
  end
data/spec/spec_helper.rb CHANGED
@@ -12,3 +12,11 @@ UNIONSTORE_KEY = 'test:unionstore'
12
12
  INTERSTORE_KEY = 'test:interstore'
13
13
  DIFFSTORE_KEY = 'test:diffstore'
14
14
 
15
+
16
+ SORT_ORDER = {:order => 'desc alpha'}
17
+ SORT_LIMIT = {:limit => [2, 2]}
18
+ SORT_BY = {:by => 'm_*'}
19
+ SORT_GET = {:get => 'spec/*/sorted'}.merge!(SORT_LIMIT)
20
+ SORT_STORE = {:store => "spec/aftersort"}.merge!(SORT_GET)
21
+
22
+
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-objects
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 5
8
- - 0
9
- version: 0.5.0
4
+ prerelease:
5
+ version: 0.5.1
10
6
  platform: ruby
11
7
  authors:
12
8
  - Nate Wiger
@@ -14,8 +10,7 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2010-11-08 00:00:00 -08:00
18
- default_executable:
13
+ date: 2011-05-23 00:00:00 Z
19
14
  dependencies:
20
15
  - !ruby/object:Gem::Dependency
21
16
  name: bacon
@@ -25,8 +20,6 @@ dependencies:
25
20
  requirements:
26
21
  - - ">="
27
22
  - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
23
  version: "0"
31
24
  type: :development
32
25
  version_requirements: *id001
@@ -38,10 +31,6 @@ dependencies:
38
31
  requirements:
39
32
  - - ">="
40
33
  - !ruby/object:Gem::Version
41
- segments:
42
- - 2
43
- - 1
44
- - 1
45
34
  version: 2.1.1
46
35
  type: :runtime
47
36
  version_requirements: *id002
@@ -54,7 +43,6 @@ extensions: []
54
43
  extra_rdoc_files:
55
44
  - README.rdoc
56
45
  files:
57
- - .gitignore
58
46
  - ATOMICITY.rdoc
59
47
  - CHANGELOG.rdoc
60
48
  - README.rdoc
@@ -80,16 +68,16 @@ files:
80
68
  - lib/redis/value.rb
81
69
  - redis-objects.gemspec
82
70
  - spec/redis_namespace_compat_spec.rb
71
+ - spec/redis_objects_active_record_spec.rb
83
72
  - spec/redis_objects_instance_spec.rb
84
73
  - spec/redis_objects_model_spec.rb
85
74
  - spec/spec_helper.rb
86
- has_rdoc: true
87
75
  homepage: http://github.com/nateware/redis-objects
88
76
  licenses: []
89
77
 
90
78
  post_install_message:
91
- rdoc_options:
92
- - --charset=UTF-8
79
+ rdoc_options: []
80
+
93
81
  require_paths:
94
82
  - lib
95
83
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -97,26 +85,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
97
85
  requirements:
98
86
  - - ">="
99
87
  - !ruby/object:Gem::Version
100
- segments:
101
- - 0
102
88
  version: "0"
103
89
  required_rubygems_version: !ruby/object:Gem::Requirement
104
90
  none: false
105
91
  requirements:
106
92
  - - ">="
107
93
  - !ruby/object:Gem::Version
108
- segments:
109
- - 0
110
94
  version: "0"
111
95
  requirements:
112
96
  - redis, v2.1.1 or greater
113
97
  rubyforge_project:
114
- rubygems_version: 1.3.7
98
+ rubygems_version: 1.7.2
115
99
  signing_key:
116
100
  specification_version: 3
117
101
  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
102
+ test_files: []
103
+
data/.gitignore DELETED
@@ -1,3 +0,0 @@
1
- *.gemspec
2
- nbproject
3
- pkg/