redis-objects 0.6.1 → 0.7.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/Rakefile CHANGED
@@ -7,7 +7,7 @@ begin
7
7
  gem.name = "redis-objects"
8
8
  gem.summary = %Q{Map Redis types directly to Ruby objects}
9
9
  gem.description = %Q{Map Redis types directly to Ruby objects. Works with any class or ORM.}
10
- gem.email = "nate@wiger.org"
10
+ gem.email = "nwiger@gmail.com"
11
11
  gem.homepage = "http://github.com/nateware/redis-objects"
12
12
  gem.authors = ["Nate Wiger"]
13
13
  gem.add_development_dependency "bacon", ">= 0"
@@ -30,7 +30,7 @@ end
30
30
 
31
31
  desc "run all the specs"
32
32
  task :test do
33
- sh "bundle exec bacon spec/*_spec.rb"
33
+ sh "bacon spec/*_spec.rb"
34
34
  end
35
35
 
36
36
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.1
1
+ 0.7.0
@@ -0,0 +1 @@
1
+ require 'redis/objects'
@@ -4,7 +4,12 @@ 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 || Redis.current
7
+ @myredis = args.first
8
+ end
9
+
10
+ # Dynamically query the handle to enable resetting midstream
11
+ def redis
12
+ @myredis || ::Redis::Objects.redis
8
13
  end
9
14
 
10
15
  alias :inspect :to_s # Ruby 1.9.2
data/lib/redis/counter.rb CHANGED
@@ -12,11 +12,11 @@ class Redis
12
12
  require 'redis/helpers/core_commands'
13
13
  include Redis::Helpers::CoreCommands
14
14
 
15
- attr_reader :key, :options, :redis
15
+ attr_reader :key, :options
16
16
  def initialize(key, *args)
17
17
  super(key, *args)
18
18
  @options[:start] ||= 0
19
- @redis.setnx(key, @options[:start]) unless @options[:start] == 0 || @options[:init] === false
19
+ redis.setnx(key, @options[:start]) unless @options[:start] == 0 || @options[:init] === false
20
20
  end
21
21
 
22
22
  # Reset the counter to its starting value. Not atomic, so use with care.
@@ -52,7 +52,7 @@ class Redis
52
52
  # method is aliased as incr() for brevity.
53
53
  def increment(by=1, &block)
54
54
  val = redis.incrby(key, by).to_i
55
- block_given? ? rewindable_block(:decrement, val, &block) : val
55
+ block_given? ? rewindable_block(:decrement, by, val, &block) : val
56
56
  end
57
57
  alias_method :incr, :increment
58
58
 
@@ -60,10 +60,10 @@ class Redis
60
60
  # a block, that block will be evaluated with the new value of the counter
61
61
  # as an argument. If the block returns nil or throws an exception, the
62
62
  # counter will automatically be incremented to its previous value. This
63
- # method is aliased as incr() for brevity.
63
+ # method is aliased as decr() for brevity.
64
64
  def decrement(by=1, &block)
65
65
  val = redis.decrby(key, by).to_i
66
- block_given? ? rewindable_block(:increment, val, &block) : val
66
+ block_given? ? rewindable_block(:increment, by, val, &block) : val
67
67
  end
68
68
  alias_method :decr, :decrement
69
69
 
@@ -85,16 +85,16 @@ class Redis
85
85
  private
86
86
 
87
87
  # Implements atomic increment/decrement blocks
88
- def rewindable_block(rewind, value, &block)
88
+ def rewindable_block(rewind, by, value, &block)
89
89
  raise ArgumentError, "Missing block to rewindable_block somehow" unless block_given?
90
90
  ret = nil
91
91
  begin
92
92
  ret = yield value
93
93
  rescue
94
- send(rewind)
94
+ send(rewind, by)
95
95
  raise
96
96
  end
97
- send(rewind) if ret.nil?
97
+ send(rewind, by) if ret.nil?
98
98
  ret
99
99
  end
100
100
  end
@@ -12,8 +12,7 @@ class Redis
12
12
  require 'redis/helpers/serialize'
13
13
  include Redis::Helpers::Serialize
14
14
 
15
- attr_reader :key, :options, :redis
16
-
15
+ attr_reader :key, :options
17
16
  def initialize(key, *args)
18
17
  super
19
18
  @options[:marshal_keys] ||= {}
@@ -35,7 +35,7 @@ class Redis
35
35
  end
36
36
 
37
37
  def expireat(unixtime)
38
- redis.expire key, unixtime
38
+ redis.expireat key, unixtime
39
39
  end
40
40
 
41
41
  def persist
@@ -12,8 +12,20 @@ class Redis
12
12
  dump(value)
13
13
  end
14
14
  end
15
-
15
+
16
16
  def from_redis(value, marshal=false)
17
+ # This was removed because we can't reliably determine
18
+ # if a person said @value = "123.4" maybe for space/etc.
19
+ #begin
20
+ # case value
21
+ # when /^\d+$/
22
+ # return Integer(value)
23
+ # when /^(?:\d+\d.\d*|\d*\.\d+)$/
24
+ # return Float(value)
25
+ # end
26
+ #rescue
27
+ # # continue below
28
+ #end
17
29
  return value unless options[:marshal] || marshal
18
30
  case value
19
31
  when Array
data/lib/redis/list.rb CHANGED
@@ -13,7 +13,7 @@ class Redis
13
13
  require 'redis/helpers/serialize'
14
14
  include Redis::Helpers::Serialize
15
15
 
16
- attr_reader :key, :options, :redis
16
+ attr_reader :key, :options
17
17
 
18
18
  # Works like push. Can chain together: list << 'a' << 'b'
19
19
  def <<(value)
@@ -21,10 +21,10 @@ class Redis
21
21
  self # for << 'a' << 'b'
22
22
  end
23
23
 
24
- # Add a member before or after pivot in the list. Redis: LINSERT
25
- def insert(where,pivot,value)
26
- redis.linsert(key,where,to_redis(pivot),to_redis(value))
27
- end
24
+ # Add a member before or after pivot in the list. Redis: LINSERT
25
+ def insert(where,pivot,value)
26
+ redis.linsert(key,where,to_redis(pivot),to_redis(value))
27
+ end
28
28
 
29
29
  # Add a member to the end of the list. Redis: RPUSH
30
30
  def push(value)
data/lib/redis/lock.rb CHANGED
@@ -11,12 +11,12 @@ class Redis
11
11
  class Lock < BaseObject
12
12
  class LockTimeout < StandardError; end #:nodoc:
13
13
 
14
- attr_reader :key, :options, :redis
14
+ attr_reader :key, :options
15
15
  def initialize(key, *args)
16
16
  super(key, *args)
17
17
  @options[:timeout] ||= 5
18
18
  @options[:init] = false if @options[:init].nil? # default :init to false
19
- @redis.setnx(key, @options[:start]) unless @options[:start] == 0 || @options[:init] === false
19
+ redis.setnx(key, @options[:start]) unless @options[:start] == 0 || @options[:init] === false
20
20
  end
21
21
 
22
22
  # Clear the lock. Should only be needed if there's a server crash
data/lib/redis/objects.rb CHANGED
@@ -1,7 +1,16 @@
1
1
  # Redis::Objects - Lightweight object layer around redis-rb
2
2
  # See README.rdoc for usage and approach.
3
3
  require 'redis'
4
+
4
5
  class Redis
6
+ autoload :Counter, 'redis/counter'
7
+ autoload :List, 'redis/list'
8
+ autoload :Lock, 'redis/lock'
9
+ autoload :Set, 'redis/set'
10
+ autoload :SortedSet, 'redis/sorted_set'
11
+ autoload :Value, 'redis/value'
12
+ autoload :HashKey, 'redis/hash_key'
13
+
5
14
  #
6
15
  # Redis::Objects enables high-performance atomic operations in your app
7
16
  # by leveraging the atomic features of the Redis server. To use Redis::Objects,
@@ -17,7 +26,7 @@ class Redis
17
26
  # lock :archive_game
18
27
  # set :player_ids
19
28
  # end
20
- #
29
+ #
21
30
  # The, you can use these counters both for bookeeping and as atomic actions:
22
31
  #
23
32
  # @game = Game.find(id)
@@ -38,30 +47,33 @@ class Redis
38
47
  module Objects
39
48
  dir = File.expand_path(__FILE__.sub(/\.rb$/,''))
40
49
 
41
- autoload :Counters, File.join(dir, 'counters')
42
- autoload :Lists, File.join(dir, 'lists')
43
- autoload :Locks, File.join(dir, 'locks')
44
- autoload :Sets, File.join(dir, 'sets')
45
- autoload :SortedSets, File.join(dir, 'sorted_sets')
46
- autoload :Values, File.join(dir, 'values')
47
- autoload :Hashes, File.join(dir, 'hashes')
50
+ autoload :Counters, 'redis/objects/counters'
51
+ autoload :Lists, 'redis/objects/lists'
52
+ autoload :Locks, 'redis/objects/locks'
53
+ autoload :Sets, 'redis/objects/sets'
54
+ autoload :SortedSets, 'redis/objects/sorted_sets'
55
+ autoload :Values, 'redis/objects/values'
56
+ autoload :Hashes, 'redis/objects/hashes'
48
57
 
49
58
  class NotConnected < StandardError; end
50
59
  class NilObjectId < StandardError; end
51
60
 
52
61
  class << self
53
- def redis=(conn) @redis = conn end
62
+ def redis=(conn)
63
+ @redis = conn
64
+ end
54
65
  def redis
55
- @redis ||= $redis || Redis.current || raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection")
66
+ @redis || $redis || Redis.current ||
67
+ raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection")
56
68
  end
57
69
 
58
70
  def included(klass)
59
71
  # Core (this file)
60
- klass.instance_variable_set('@redis', @redis)
72
+ klass.instance_variable_set('@redis', nil)
61
73
  klass.instance_variable_set('@redis_objects', {})
62
74
  klass.send :include, InstanceMethods
63
75
  klass.extend ClassMethods
64
-
76
+
65
77
  # Pull in each object type
66
78
  klass.send :include, Redis::Objects::Counters
67
79
  klass.send :include, Redis::Objects::Lists
@@ -75,9 +87,17 @@ class Redis
75
87
 
76
88
  # Class methods that appear in your class when you include Redis::Objects.
77
89
  module ClassMethods
78
- attr_writer :redis
79
- attr_accessor :redis_objects
80
- def redis() @redis ||= Objects.redis end
90
+ # Enable per-class connections (eg, User and Post can use diff redis-server)
91
+ attr_writer :redis
92
+ def redis
93
+ @redis || Objects.redis
94
+ end
95
+
96
+ # Internal list of objects
97
+ attr_writer :redis_objects
98
+ def redis_objects
99
+ @redis_objects ||= {}
100
+ end
81
101
 
82
102
  # Set the Redis redis_prefix to use. Defaults to model_name
83
103
  def redis_prefix=(redis_prefix) @redis_prefix = redis_prefix end
@@ -22,7 +22,7 @@ class Redis
22
22
  def counter(name, options={})
23
23
  options[:start] ||= 0
24
24
  options[:type] ||= options[:start] == 0 ? :increment : :decrement
25
- @redis_objects[name.to_sym] = options.merge(:type => :counter)
25
+ redis_objects[name.to_sym] = options.merge(:type => :counter)
26
26
  klass_name = '::' + self.name
27
27
  if options[:global]
28
28
  instance_eval <<-EndMethods
@@ -60,7 +60,7 @@ class Redis
60
60
  verify_counter_defined!(name, id)
61
61
  initialize_counter!(name, id)
62
62
  value = redis.incrby(redis_field_key(name, id), by).to_i
63
- block_given? ? rewindable_block(:decrement_counter, name, id, value, &block) : value
63
+ block_given? ? rewindable_block(:decrement_counter, name, id, by, value, &block) : value
64
64
  end
65
65
 
66
66
  # Decrement a counter with the specified name and id. Accepts a block
@@ -71,13 +71,13 @@ class Redis
71
71
  verify_counter_defined!(name, id)
72
72
  initialize_counter!(name, id)
73
73
  value = redis.decrby(redis_field_key(name, id), by).to_i
74
- block_given? ? rewindable_block(:increment_counter, name, id, value, &block) : value
74
+ block_given? ? rewindable_block(:increment_counter, name, id, by, value, &block) : value
75
75
  end
76
76
 
77
77
  # Reset a counter to its starting value.
78
78
  def reset_counter(name, id=nil, to=nil)
79
79
  verify_counter_defined!(name, id)
80
- to = @redis_objects[name][:start] if to.nil?
80
+ to = redis_objects[name][:start] if to.nil?
81
81
  redis.set(redis_field_key(name, id), to.to_i)
82
82
  true
83
83
  end
@@ -85,7 +85,7 @@ class Redis
85
85
  # Set a counter to its starting value and return the old value.
86
86
  def getset_counter(name, id=nil, to=nil)
87
87
  verify_counter_defined!(name, id)
88
- to = @redis_objects[name][:start] if to.nil?
88
+ to = redis_objects[name][:start] if to.nil?
89
89
  redis.getset(redis_field_key(name, id), to.to_i).to_i
90
90
  end
91
91
 
@@ -93,35 +93,35 @@ class Redis
93
93
 
94
94
  def verify_counter_defined!(name, id) #:nodoc:
95
95
  raise NoMethodError, "Undefined counter :#{name} for class #{self.name}" unless counter_defined?(name)
96
- if id.nil? and !@redis_objects[name][:global]
96
+ if id.nil? and !redis_objects[name][:global]
97
97
  raise Redis::Objects::MissingID, "Missing ID for non-global counter #{self.name}##{name}"
98
98
  end
99
99
  end
100
100
 
101
101
  def counter_defined?(name) #:nodoc:
102
- @redis_objects && @redis_objects.has_key?(name)
102
+ redis_objects && redis_objects.has_key?(name)
103
103
  end
104
104
 
105
105
  def initialize_counter!(name, id) #:nodoc:
106
106
  key = redis_field_key(name, id)
107
107
  unless @initialized_counters[key]
108
- redis.setnx(key, @redis_objects[name][:start])
108
+ redis.setnx(key, redis_objects[name][:start])
109
109
  end
110
110
  @initialized_counters[key] = true
111
111
  end
112
112
 
113
113
  # Implements increment/decrement blocks on a class level
114
- def rewindable_block(rewind, name, id, value, &block) #:nodoc:
114
+ def rewindable_block(rewind, name, id, by, value, &block) #:nodoc:
115
115
  # Unfortunately this is almost exactly duplicated from Redis::Counter
116
116
  raise ArgumentError, "Missing block to rewindable_block somehow" unless block_given?
117
117
  ret = nil
118
118
  begin
119
119
  ret = yield value
120
120
  rescue
121
- send(rewind, name, id)
121
+ send(rewind, name, id, by)
122
122
  raise
123
123
  end
124
- send(rewind, name, id) if ret.nil?
124
+ send(rewind, name, id, by) if ret.nil?
125
125
  ret
126
126
  end
127
127
  end
@@ -14,7 +14,7 @@ class Redis
14
14
  # Define a new hash key. It will function like a regular instance
15
15
  # method, so it can be used alongside ActiveRecord, DataMapper, etc.
16
16
  def hash_key(name, options={})
17
- @redis_objects[name.to_sym] = options.merge(:type => :dict)
17
+ redis_objects[name.to_sym] = options.merge(:type => :dict)
18
18
  klass_name = '::' + self.name
19
19
  if options[:global]
20
20
  instance_eval <<-EndMethods
@@ -14,7 +14,7 @@ class Redis
14
14
  # Define a new list. It will function like a regular instance
15
15
  # method, so it can be used alongside ActiveRecord, DataMapper, etc.
16
16
  def list(name, options={})
17
- @redis_objects[name.to_sym] = options.merge(:type => :list)
17
+ redis_objects[name.to_sym] = options.merge(:type => :list)
18
18
  klass_name = '::' + self.name
19
19
  if options[:global]
20
20
  instance_eval <<-EndMethods
@@ -42,4 +42,4 @@ class Redis
42
42
  end
43
43
  end
44
44
  end
45
- end
45
+ end
@@ -17,7 +17,7 @@ class Redis
17
17
  def lock(name, options={})
18
18
  options[:timeout] ||= 5 # seconds
19
19
  lock_name = "#{name}_lock"
20
- @redis_objects[lock_name.to_sym] = options.merge(:type => :lock)
20
+ redis_objects[lock_name.to_sym] = options.merge(:type => :lock)
21
21
  klass_name = '::' + self.name
22
22
  if options[:global]
23
23
  instance_eval <<-EndMethods
@@ -46,7 +46,7 @@ class Redis
46
46
  verify_lock_defined!(name)
47
47
  raise ArgumentError, "Missing block to #{self.name}.obtain_lock" unless block_given?
48
48
  lock_name = "#{name}_lock"
49
- Redis::Lock.new(redis_field_key(lock_name, id), redis, @redis_objects[lock_name.to_sym]).lock(&block)
49
+ Redis::Lock.new(redis_field_key(lock_name, id), redis, redis_objects[lock_name.to_sym]).lock(&block)
50
50
  end
51
51
 
52
52
  # Clear the lock. Use with care - usually only in an Admin page to clear
@@ -57,9 +57,9 @@ class Redis
57
57
  end
58
58
 
59
59
  private
60
-
60
+
61
61
  def verify_lock_defined!(name)
62
- unless @redis_objects.has_key?("#{name}_lock".to_sym)
62
+ unless redis_objects.has_key?("#{name}_lock".to_sym)
63
63
  raise Redis::Objects::UndefinedLock, "Undefined lock :#{name} for class #{self.name}"
64
64
  end
65
65
  end
@@ -14,7 +14,7 @@ class Redis
14
14
  # Define a new list. It will function like a regular instance
15
15
  # method, so it can be used alongside ActiveRecord, DataMapper, etc.
16
16
  def set(name, options={})
17
- @redis_objects[name.to_sym] = options.merge(:type => :set)
17
+ redis_objects[name.to_sym] = options.merge(:type => :set)
18
18
  klass_name = '::' + self.name
19
19
  if options[:global]
20
20
  instance_eval <<-EndMethods
@@ -34,7 +34,7 @@ class Redis
34
34
  end
35
35
  EndMethods
36
36
  end
37
-
37
+
38
38
  end
39
39
  end
40
40
 
@@ -43,4 +43,4 @@ class Redis
43
43
  end
44
44
  end
45
45
  end
46
- end
46
+ end
@@ -14,7 +14,7 @@ class Redis
14
14
  # Define a new list. It will function like a regular instance
15
15
  # method, so it can be used alongside ActiveRecord, DataMapper, etc.
16
16
  def sorted_set(name, options={})
17
- @redis_objects[name.to_sym] = options.merge(:type => :sorted_set)
17
+ redis_objects[name.to_sym] = options.merge(:type => :sorted_set)
18
18
  klass_name = '::' + self.name
19
19
  if options[:global]
20
20
  instance_eval <<-EndMethods
@@ -42,4 +42,4 @@ class Redis
42
42
  end
43
43
  end
44
44
  end
45
- end
45
+ end