redis-objects 0.5.0 → 0.5.1

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,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/