Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for complex data types in HashKey (w/specs) #14

Closed
wants to merge 6 commits into from
14 changes: 7 additions & 7 deletions lib/redis/hash_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class HashKey < BaseObject
require 'redis/helpers/serialize'
include Redis::Helpers::Serialize

attr_reader :key, :redis
attr_reader :key, :options, :redis

# Sets a field to value
def []=(field, value)
Expand All @@ -24,12 +24,12 @@ def [](field)

# Redis: HSET
def store(field, value)
redis.hset(key, field, value)
redis.hset(key, field, to_redis(value))
end

# Redis: HGET
def fetch(field)
redis.hget(key, field)
from_redis redis.hget(key, field)
end

# Verify that a field exists. Redis: HEXISTS
Expand All @@ -52,13 +52,13 @@ def keys

# Return all the values of the hash. Redis: HVALS
def values
redis.hvals(key)
from_redis redis.hvals(key)
end
alias_method :vals, :values

# Retrieve the entire hash. Redis: HGETALL
def all
redis.hgetall(key)
from_redis redis.hgetall(key)
end
alias_method :clone, :all

Expand Down Expand Up @@ -97,15 +97,15 @@ def clear
# Set keys in bulk, takes a hash of field/values {'field1' => 'val1'}. Redis: HMSET
def bulk_set(*args)
raise ArgumentError, "Argument to bulk_set must be hash of key/value pairs" unless args.last.is_a?(::Hash)
redis.hmset(key, *args.last.inject([]){ |arr,kv| arr + kv })
redis.hmset(key, *args.last.inject([]){ |arr,kv| arr + [kv[0], to_redis(kv[1])] })
end

# Get keys in bulk, takes an array of fields as arguments. Redis: HMGET
def bulk_get(*fields)
hsh = {}
res = redis.hmget(key, *fields.flatten)
fields.each do |k|
hsh[k] = res.shift
hsh[k] = from_redis(res.shift)
end
hsh
end
Expand Down
2 changes: 2 additions & 0 deletions lib/redis/helpers/serialize.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def from_redis(value)
case value
when Array
value.collect{|v| from_redis(v)}
when Hash
value.inject({}) { |h, (k, v)| h[k] = from_redis(v); h }
else
restore(value) rescue value
end
Expand Down
18 changes: 15 additions & 3 deletions lib/redis/objects/counters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,16 @@ def reset_counter(name, id=nil, to=nil)
private

def verify_counter_defined!(name, id) #:nodoc:
raise Redis::Objects::UndefinedCounter, "Undefined counter :#{name} for class #{self.name}" unless @redis_objects.has_key?(name)
raise Redis::Objects::UndefinedCounter, "Undefined counter :#{name} for class #{self.name}" unless counter_defined?(name)
if id.nil? and !@redis_objects[name][:global]
raise Redis::Objects::MissingID, "Missing ID for non-global counter #{self.name}##{name}"
end
end

def counter_defined?(name) #:nodoc:
@redis_objects && @redis_objects.has_key?(name)
end

def initialize_counter!(name, id) #:nodoc:
key = redis_field_key(name, id)
unless @initialized_counters[key]
Expand Down Expand Up @@ -117,14 +121,22 @@ module InstanceMethods
# It is more efficient to use increment_[counter_name] directly.
# This is mainly just for completeness to override ActiveRecord.
def increment(name, by=1)
send(name).increment(by)
if self.class.send("counter_defined?", name)
send(name).increment(by)
else
super
end
end

# Decrement a counter.
# It is more efficient to use increment_[counter_name] directly.
# This is mainly just for completeness to override ActiveRecord.
def decrement(name, by=1)
send(name).decrement(by)
if self.class.send("counter_defined?", name)
send(name).decrement(by)
else
super
end
end
end
end
Expand Down
38 changes: 38 additions & 0 deletions spec/redis_objects_instance_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,44 @@
@hash.clear
end

it "should handle complex marshaled values" do
@hash.options[:marshal] = true
@hash['abc'].should == nil
@hash['abc'] = {:json => 'data'}
@hash['abc'].should == {:json => 'data'}

# no marshaling
@hash.options[:marshal] = false
v = {:json => 'data'}
@hash['abc'] = v
@hash['abc'].should == v.to_s

@hash.options[:marshal] = true
@hash['abc'] = [[1,2], {:t3 => 4}]
@hash['abc'].should == [[1,2], {:t3 => 4}]
@hash.fetch('abc').should == [[1,2], {:t3 => 4}]
@hash.delete('abc').should == 1
@hash.fetch('abc').should.be.nil

@hash.options[:marshal] = true
@hash.bulk_set('abc' => [[1,2], {:t3 => 4}], 'def' => [[6,8], {:t4 => 8}])
hsh = @hash.bulk_get('abc', 'def', 'foo')
hsh['abc'].should == [[1,2], {:t3 => 4}]
hsh['def'].should == [[6,8], {:t4 => 8}]
hsh['foo'].should.be.nil

hsh = @hash.all
hsh['abc'].should == [[1,2], {:t3 => 4}]
hsh['def'].should == [[6,8], {:t4 => 8}]

@hash.values.should == [[[1,2], {:t3 => 4}], [[6,8], {:t4 => 8}]]

@hash.delete('def').should == 1
@hash.delete('abc').should == 1

@hash.options[:marshal] = false
end

it "should get and set values" do
@hash['foo'] = 'bar'
@hash['foo'].should == 'bar'
Expand Down
25 changes: 25 additions & 0 deletions spec/redis_objects_model_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ class CustomRoster < Roster
counter :special # New
end

class MethodRoster
def increment(attribute, by=1)
42
end

def initialize(id=1) @id = id end
def id; @id; end
end

class CustomMethodRoster < MethodRoster
include Redis::Objects

attr_accessor :counter
counter :basic
end

describe Redis::Objects do
before do
Expand Down Expand Up @@ -748,4 +763,14 @@ class CustomRoster < Roster
it "should handle new subclass objects" do
@custom_roster.special.increment.should == 1
end

it "should allow passing of increment/decrement to super class" do
@custom_method_roster = CustomMethodRoster.new
@custom_method_roster.counter.should.be.nil

@custom_method_roster.increment(:counter).should == 42

@custom_method_roster.increment(:basic).should == 1
@custom_method_roster.basic.should.be.kind_of(Redis::Counter)
end
end