redis-native_hash 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem "redis", ">= 2.0.0"
6
+
7
+ # Add dependencies to develop your gem here.
8
+ # Include everything needed to run rake, tests, features, etc.
9
+ group :development do
10
+ gem "rspec", "~> 2.3.0"
11
+ gem "bundler", "~> 1.0.0"
12
+ gem "jeweler", "~> 1.6.2"
13
+ gem "rcov", ">= 0"
14
+ end
15
+
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ git (1.2.5)
6
+ jeweler (1.6.2)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rake (0.9.2)
11
+ rcov (0.9.9)
12
+ redis (2.2.1)
13
+ rspec (2.3.0)
14
+ rspec-core (~> 2.3.0)
15
+ rspec-expectations (~> 2.3.0)
16
+ rspec-mocks (~> 2.3.0)
17
+ rspec-core (2.3.1)
18
+ rspec-expectations (2.3.0)
19
+ diff-lcs (~> 1.1.2)
20
+ rspec-mocks (2.3.0)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ bundler (~> 1.0.0)
27
+ jeweler (~> 1.6.2)
28
+ rcov
29
+ redis (>= 2.0.0)
30
+ rspec (~> 2.3.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011 Lyconic
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.mkd ADDED
@@ -0,0 +1,19 @@
1
+ = redis-native_hash
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to redis-native_hash
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 Lyconic. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "redis-native_hash"
18
+ gem.homepage = "http://github.com/carlzulauf/redis-native_hash"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{ruby-hash-to-redis-hash mapping}
21
+ gem.description = %Q{ruby-hash-to-redis-hash mapping}
22
+ gem.email = "czulauf@lyconic.com"
23
+ gem.authors = ["Carl Zulauf", "Adam Lassek"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "redis-hash #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
50
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,9 @@
1
+ class Hash
2
+ def stringify_keys!
3
+ keys.each do |key|
4
+ self[key.to_s] = delete(key)
5
+ end
6
+ self
7
+ end
8
+ end
9
+
@@ -0,0 +1,34 @@
1
+ module Rack
2
+ module Session
3
+ class RedisHash < Abstract::ID
4
+
5
+ def get_session(env, sid)
6
+ session = Redis::NativeHash.find session_prefix => sid
7
+ unless sid and session
8
+ env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
9
+ session = Redis::NativeHash.new session_prefix
10
+ sid = session.key
11
+ end
12
+ return [sid, session]
13
+ end
14
+
15
+ def set_session(env, session_id, session, options)
16
+ if options[:drop]
17
+ session.destroy
18
+ return false if options[:drop]
19
+ end
20
+ if options[:renew]
21
+ session_id = session.renew_key
22
+ end
23
+ session.save
24
+ return session_id
25
+ end
26
+
27
+ def session_prefix
28
+ :rack_session
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,20 @@
1
+ class Redis
2
+ class Marshal
3
+ def self.dump(value)
4
+ case value
5
+ when String
6
+ value
7
+ when Fixnum
8
+ value
9
+ else
10
+ ::Marshal.dump(value)
11
+ end
12
+ end
13
+
14
+ def self.load(value)
15
+ return value unless value.start_with?("\004")
16
+ ::Marshal.load(value) rescue value
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,205 @@
1
+ require 'redis'
2
+ require 'core_ext/hash' unless defined?(ActiveSupport)
3
+ require 'redis/marshal'
4
+ require 'redis/tracked_hash'
5
+
6
+ if defined?(Rack::Session)
7
+ require "rack/session/abstract/id"
8
+ require 'rack/session/redis_hash'
9
+ end
10
+
11
+ require 'securerandom'
12
+
13
+ class Redis
14
+ class NativeHash < TrackedHash
15
+
16
+ attr_accessor :namespace
17
+
18
+ def initialize(*args)
19
+ super(nil)
20
+ track!
21
+ if args.first.kind_of?(String) or args.first.kind_of?(Symbol)
22
+ self.namespace = args.shift
23
+ elsif !self.instance_of?(NativeHash) # use class name as default namespace for user defined classes
24
+ self.namespace = self.class.to_s.downcase
25
+ end
26
+ data = args.shift
27
+ update(data) if data.kind_of?(Hash)
28
+ end
29
+
30
+ def []=(key, value)
31
+ super(convert_key(key), value)
32
+ end
33
+ alias_method :store, :[]=
34
+
35
+ def fetch(key, *extras)
36
+ super(convert_key(key),*extras)
37
+ end
38
+
39
+ def [](key)
40
+ super convert_key(key)
41
+ end
42
+
43
+ def key?(key)
44
+ super convert_key(key)
45
+ end
46
+ alias_method :include?, :key?
47
+ alias_method :has_key?, :key?
48
+ alias_method :member?, :key?
49
+
50
+ def delete(key)
51
+ super convert_key(key)
52
+ end
53
+
54
+ def values_at(*indices)
55
+ indices.collect { |key| self[ convert_key(key) ] }
56
+ end
57
+
58
+ def key
59
+ @key ||= self.class.generate_key
60
+ end
61
+
62
+ def key=(new_key)
63
+ renew_key(new_key)
64
+ end
65
+
66
+ attr_writer :version
67
+ def version
68
+ @version ||= self.class.generate_key
69
+ end
70
+
71
+ def save( attempt = 0 )
72
+ fail "Unable to save Redis::Hash after max attempts." if attempt > 5
73
+ redis.watch redis_key
74
+ latest_version = redis.hget(redis_key, "__version")
75
+ reload! unless ( latest_version.nil? || latest_version == self.version )
76
+ self.version = nil # generate new version token
77
+ changed_keys = (self.changed + self.added).uniq
78
+ changes = []
79
+ changed_keys.each do |key|
80
+ changes.push( key, Redis::Marshal.dump(self[key]) )
81
+ end
82
+ deleted_keys = self.deleted
83
+ unless deleted_keys.empty? and changes.empty?
84
+ success = redis.multi do
85
+ redis.hmset( redis_key, *changes.push("__version", self.version) ) unless changes.empty?
86
+ deleted_keys.each { |key| redis.hdel( redis_key, key) }
87
+ end
88
+ if success
89
+ untrack!; track! #reset!
90
+ else
91
+ save( attempt + 1 )
92
+ end
93
+ else
94
+ redis.unwatch
95
+ end
96
+ end
97
+
98
+ def update(data)
99
+ v = case data
100
+ when self.class
101
+ data.version
102
+ when ::Hash
103
+ data.delete("__version")
104
+ end
105
+ self.version = v unless v.nil?
106
+ super(data.stringify_keys!)
107
+ end
108
+
109
+ def reload!
110
+ self.update( self.class.find( {namespace=>key} ) )
111
+ end
112
+ alias_method :reload, :reload!
113
+
114
+ def destroy
115
+ redis.del( redis_key )
116
+ untrack!
117
+ clear
118
+ self.key = nil
119
+ end
120
+
121
+ def renew_key(new_key = nil)
122
+ unless @key.nil? || @key == new_key
123
+ redis.del( redis_key )
124
+ original.clear
125
+ end
126
+ @key = new_key
127
+ key
128
+ end
129
+
130
+ def self.redis
131
+ @@redis ||= Redis.new
132
+ end
133
+
134
+ def self.redis=(resource)
135
+ @@redis = resource
136
+ end
137
+
138
+ def self.generate_key
139
+ t = Time.now
140
+ t.strftime('%Y%m%d%H%M%S.') + t.usec.to_s.rjust(6,'0') + '.' + SecureRandom.hex(16)
141
+ end
142
+
143
+ def self.find(params)
144
+ case params
145
+ when Hash
146
+ hashes = []
147
+ params.each_pair do |namespace, key|
148
+ result = fetch_values( "#{namespace}:#{key}" )
149
+ unless result.empty?
150
+ hashes << build(namespace,key,result)
151
+ end
152
+ end
153
+ unless hashes.empty?
154
+ hashes.size == 1 ? hashes.first : hashes
155
+ else
156
+ nil
157
+ end
158
+ when String
159
+ unless self.instance_of?(NativeHash)
160
+ result = fetch_values( "#{self.new.namespace}:#{params}" )
161
+ else
162
+ result = fetch_values(params)
163
+ end
164
+ result.empty? ? nil : build(nil,params,result)
165
+ end
166
+ end
167
+
168
+ def self.build(namespace, key, values)
169
+ h = self.new
170
+ h.namespace = namespace
171
+ h.key = key
172
+ h.populate(values)
173
+ h
174
+ end
175
+
176
+ def self.fetch_values(key)
177
+ results = redis.hgetall(key)
178
+ results.each_pair { |key,value| results[key] = Redis::Marshal.load(value) }
179
+ end
180
+
181
+ def self.attr_persist(*attributes)
182
+ attributes.each do |attr|
183
+ class_eval <<-EOS
184
+ def #{attr}=(value)
185
+ self["#{attr}"] = value
186
+ end
187
+
188
+ def #{attr}
189
+ self["#{attr}"]
190
+ end
191
+ EOS
192
+ end
193
+ end
194
+
195
+ protected
196
+ def redis; self.class.redis; end
197
+ def redis_key
198
+ namespace.nil? ? key : "#{namespace}:#{key}"
199
+ end
200
+ def convert_key(key)
201
+ key.to_s
202
+ end
203
+ end
204
+ end
205
+
@@ -0,0 +1,56 @@
1
+ class Redis
2
+ class TrackedHash < Hash
3
+
4
+ def original
5
+ @original ||= self.dup
6
+ end
7
+ alias_method :track, :original
8
+ alias_method :track!, :original
9
+
10
+ def untrack
11
+ @original = nil
12
+ end
13
+ alias_method :untrack!, :untrack
14
+
15
+ def retrack
16
+ untrack!
17
+ track!
18
+ end
19
+ alias_method :retrack!, :retrack
20
+
21
+ def changed
22
+ changes = keys.select do |key|
23
+ self[key] != original[key]
24
+ end
25
+ end
26
+
27
+ def deleted
28
+ original.keys - self.keys
29
+ end
30
+
31
+ def added
32
+ self.keys - original.keys
33
+ end
34
+
35
+ def populate(other_hash)
36
+ update(other_hash)
37
+ retrack!
38
+ end
39
+
40
+ def update(other_hash)
41
+ if other_hash.kind_of?(TrackedHash)
42
+ other_original = other_hash.original
43
+ other_hash.instance_variable_set('@original',original)
44
+ other_changed = other_hash.changed
45
+ other_hash.deleted.each { |key| delete(key) }
46
+ other_hash.instance_variable_set('@original',other_original)
47
+ updates = Hash[ other_changed.map { |k| [k, other_hash[k]] } ]
48
+ super( updates )
49
+ else
50
+ super
51
+ end
52
+ end
53
+ alias_method :merge!, :update
54
+
55
+ end
56
+ end
@@ -0,0 +1,70 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{redis-native_hash}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Carl Zulauf", "Adam Lassek"]
12
+ s.date = %q{2011-07-13}
13
+ s.description = %q{ruby-hash-to-redis-hash mapping}
14
+ s.email = %q{czulauf@lyconic.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.mkd"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.mkd",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "lib/core_ext/hash.rb",
29
+ "lib/rack/session/redis_hash.rb",
30
+ "lib/redis/marshal.rb",
31
+ "lib/redis/native_hash.rb",
32
+ "lib/redis/tracked_hash.rb",
33
+ "redis-native_hash.gemspec",
34
+ "spec/redis-hash_spec.rb",
35
+ "spec/redis/redis_hash_spec.rb",
36
+ "spec/spec_helper.rb",
37
+ "spec/tracked_hash_spec.rb",
38
+ "spec/user_defined/user_spec.rb"
39
+ ]
40
+ s.homepage = %q{http://github.com/carlzulauf/redis-native_hash}
41
+ s.licenses = ["MIT"]
42
+ s.require_paths = ["lib"]
43
+ s.rubygems_version = %q{1.6.2}
44
+ s.summary = %q{ruby-hash-to-redis-hash mapping}
45
+
46
+ if s.respond_to? :specification_version then
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<redis>, [">= 2.0.0"])
51
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
52
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
53
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
54
+ s.add_development_dependency(%q<rcov>, [">= 0"])
55
+ else
56
+ s.add_dependency(%q<redis>, [">= 2.0.0"])
57
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
58
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
59
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
60
+ s.add_dependency(%q<rcov>, [">= 0"])
61
+ end
62
+ else
63
+ s.add_dependency(%q<redis>, [">= 2.0.0"])
64
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
65
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
66
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
67
+ s.add_dependency(%q<rcov>, [">= 0"])
68
+ end
69
+ end
70
+
@@ -0,0 +1,141 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Redis::NativeHash do
4
+ before :each do
5
+ @hash = Redis::NativeHash.new :test, "foo" => "bar"
6
+ @hash.save
7
+ end
8
+
9
+ describe "#save" do
10
+ it "should presist changes to existing hash key" do
11
+ @hash["foo"] = "something else"
12
+ @hash.save
13
+ hash = Redis::NativeHash.find :test => @hash.key
14
+ hash["foo"].should == "something else"
15
+ end
16
+ it "should persist new hash keys" do
17
+ @hash["yin"] = "yang"
18
+ @hash.save
19
+ hash = Redis::NativeHash.find :test => @hash.key
20
+ hash["yin"].should == "yang"
21
+ end
22
+ it "should remove deleted keys from redis" do
23
+ @hash["yin"] = "yang"
24
+ @hash.delete("foo")
25
+ @hash["foo"].should == nil
26
+ @hash.save
27
+ hash = Redis::NativeHash.find :test => @hash.key
28
+ hash["foo"].should == nil
29
+ end
30
+ it "should respect changes made since last read from redis" do
31
+ concurrent_edit = Redis::NativeHash.find :test => @hash.key
32
+ concurrent_edit["foo"] = "race value"
33
+ concurrent_edit.save
34
+ @hash["yin"] = "yang"
35
+ @hash["foo"] = "bad value"
36
+ @hash.save
37
+ hash = Redis::NativeHash.find :test => @hash.key
38
+ hash["foo"].should == "race value"
39
+ hash["yin"].should == "yang"
40
+ end
41
+ it "should respect removed hash keys since last read" do
42
+ concurrent_edit = Redis::NativeHash.find :test => @hash.key
43
+ concurrent_edit["yin"] = "yang"
44
+ concurrent_edit.delete("foo")
45
+ concurrent_edit.save
46
+ @hash["foo"] = "bad value"
47
+ @hash.save
48
+ hash = Redis::NativeHash.find :test => @hash.key
49
+ hash["foo"].should == nil
50
+ hash["yin"].should == "yang"
51
+ end
52
+ it "should allow overwrite of concurrent edit after #reload! is called" do
53
+ concurrent_edit = Redis::NativeHash.find :test => @hash.key
54
+ concurrent_edit["yin"] = "yang"
55
+ concurrent_edit.delete("foo")
56
+ concurrent_edit.save
57
+ @hash.reload!
58
+ @hash["foo"].should == nil
59
+ @hash["foo"] = "good value"
60
+ @hash.save
61
+ hash = Redis::NativeHash.find :test => @hash.key
62
+ hash["foo"].should == "good value"
63
+ end
64
+ it "should treat string and symbolic keys the same" do
65
+ @hash[:foo].should == "bar"
66
+ @hash[:test] = "good value"
67
+ @hash["test"].should == "good value"
68
+ @hash.save
69
+ hash = Redis::NativeHash.find :test => @hash.key
70
+ hash[:test].should == "good value"
71
+ hash["test"].should == "good value"
72
+ end
73
+ it "should properly store nested hashes" do
74
+ @hash[:test] = { :foo => :bar, :x => { :y => "z" } }
75
+ @hash[:test][:x][:y].should == "z"
76
+ @hash.save
77
+ hash = Redis::NativeHash.find :test => @hash.key
78
+ hash[:test][:foo].should == :bar
79
+ hash[:test][:x][:y].should == "z"
80
+ end
81
+ end
82
+
83
+ describe "#renew" do
84
+ it "should generate a new key" do
85
+ old_key = @hash.key
86
+ new_key = @hash.renew_key
87
+ @hash.key.should eq(new_key)
88
+ @hash.key.should_not eq(old_key)
89
+ end
90
+ it "should remove the old hash from redis" do
91
+ old_key = @hash.key
92
+ namespace = @hash.namespace
93
+ @hash.renew_key
94
+ hash = Redis::NativeHash.find namespace => old_key
95
+ hash.should be_nil
96
+ end
97
+ it "should not persist the hash under the new key until #save is called" do
98
+ @hash["good key"] = "good value"
99
+ key = @hash.renew_key
100
+ bad_hash = Redis::NativeHash.find :test => key
101
+ bad_hash.should be_nil
102
+ @hash.save
103
+ good_hash = Redis::NativeHash.find :test => key
104
+ good_hash["good key"].should eq("good value")
105
+ good_hash["foo"].should eq("bar")
106
+ end
107
+ end
108
+
109
+ describe "#key=" do
110
+ it "should allow an arbitrary key to be used" do
111
+ @hash.key = "blah@blah.com"
112
+ @hash.save
113
+ a_hash = Redis::NativeHash.find :test => "blah@blah.com"
114
+ a_hash["foo"].should eq('bar')
115
+ end
116
+ it "should not leave the old hash behind when the key is changed" do
117
+ old_key = @hash.key
118
+ @hash.key = "carl@linkleaf.com"
119
+ @hash.save
120
+ bad_hash = Redis::NativeHash.find :test => old_key
121
+ bad_hash.should be_nil
122
+ end
123
+ end
124
+
125
+ describe ".find" do
126
+ it "should find an existing redis hash" do
127
+ hash = Redis::NativeHash.find :test => @hash.key
128
+ hash["foo"].should == "bar"
129
+ end
130
+ it "should return nil when hash not found" do
131
+ hash = Redis::NativeHash.find :foo => :doesnt_exist
132
+ hash.should == nil
133
+ end
134
+ end
135
+
136
+
137
+ after :each do
138
+ @hash.destroy
139
+ end
140
+ end
141
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Redis::NativeHash" do
4
+ it "should pass" do
5
+ end
6
+ end
7
+
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'redis/native_hash'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
13
+
@@ -0,0 +1,85 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Redis::TrackedHash do
4
+ before :each do
5
+ @hash = Redis::TrackedHash[:a=>33,:b=>34,:c=>35,:d=>36]
6
+ end
7
+
8
+ describe "#added" do
9
+ it "should track new keys added" do
10
+ @hash.track!
11
+ @hash[:dolphin] = "value"
12
+ @hash.added.should == [:dolphin]
13
+ end
14
+ it "should not track additions if #track not called" do
15
+ @hash[:dolphin] = "mammal"
16
+ @hash.added.should == []
17
+ end
18
+ end
19
+
20
+ describe "#changed" do
21
+ it "tracks changes to existing keys" do
22
+ @hash.track!
23
+ @hash[:b] += 2
24
+ @hash.changed.should == [:b]
25
+ end
26
+ it "doesn't track changes if #track has not been called" do
27
+ @hash[:b] += 3
28
+ @hash.changed.should == []
29
+ end
30
+ it "should track new keys" do
31
+ @hash.track!
32
+ @hash[:test] = 47
33
+ @hash.changed.should == [:test]
34
+ end
35
+ end
36
+
37
+ describe "#deleted" do
38
+ it "should track when you delete a key" do
39
+ @hash.track!
40
+ @hash.delete(:b)
41
+ @hash.deleted.should == [:b]
42
+ end
43
+ it "should allow you to assign to a key that has been deleted" do
44
+ @hash.track!
45
+ @hash.delete(:b)
46
+ @hash[:b] = "new value"
47
+ @hash.deleted.should == []
48
+ @hash[:b].should == "new value"
49
+ end
50
+ end
51
+
52
+ describe "#merge!" do
53
+ before :each do
54
+ @other_hash = @hash.dup
55
+ @hash.track!; @other_hash.track!
56
+ end
57
+ it "maintains change from both Redis::TrackedHash" do
58
+ @hash[:a] = 64; @other_hash[:c]=96
59
+ @hash.merge!(@other_hash).should == {:a=>64,:b=>34,:c=>96,:d=>36}
60
+ end
61
+ it "gives precedence to changes in other_hash" do
62
+ @hash[:b] = 14; @other_hash[:b] = 77
63
+ @hash.merge!(@other_hash)
64
+ @hash[:b].should == 77
65
+ end
66
+ it "always deletes keys deleted in other_hash" do
67
+ @other_hash.delete(:d)
68
+ @hash[:d] = "foo"
69
+ @hash.merge!(@other_hash)
70
+ @hash.has_key?(:d).should == false
71
+ end
72
+ it "allows assignment to a key that has been deleted after a #merge" do
73
+ @other_hash.delete(:d)
74
+ @hash.merge!(@other_hash)
75
+ @hash[:d] = 77
76
+ @hash.deleted.should == []
77
+ @hash[:d].should == 77
78
+ end
79
+ it "should merge everything from a regular hash" do
80
+ @hash.merge!( { :c => "bar", :foo => "poo" } )
81
+ @hash.changed.should include(:c, :foo)
82
+ end
83
+ end
84
+ end
85
+
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ class User < Redis::NativeHash
4
+ attr_persist :username, :password, :email
5
+ #attr_index :username
6
+ end
7
+
8
+ describe User do
9
+ before :each do
10
+ @user = User.new :username => "test", :email => "test@test.com", :random_key => "random value"
11
+ @user.save
12
+ end
13
+
14
+ describe ".find" do
15
+ it "should find the user by key" do
16
+ user = User.find(@user.key)
17
+ user.username.should == "test"
18
+ user.email.should == "test@test.com"
19
+ user[:random_key].should == "random value"
20
+ end
21
+ end
22
+
23
+ after :each do
24
+ @user.destroy
25
+ end
26
+ end
27
+
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-native_hash
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Carl Zulauf
9
+ - Adam Lassek
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-07-13 00:00:00.000000000 -05:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: redis
18
+ requirement: &15185560 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: 2.0.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *15185560
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: &15168640 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 2.3.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *15168640
38
+ - !ruby/object:Gem::Dependency
39
+ name: bundler
40
+ requirement: &15168040 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *15168040
49
+ - !ruby/object:Gem::Dependency
50
+ name: jeweler
51
+ requirement: &15167440 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.6.2
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *15167440
60
+ - !ruby/object:Gem::Dependency
61
+ name: rcov
62
+ requirement: &15166840 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *15166840
71
+ description: ruby-hash-to-redis-hash mapping
72
+ email: czulauf@lyconic.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files:
76
+ - LICENSE.txt
77
+ - README.mkd
78
+ files:
79
+ - .document
80
+ - .rspec
81
+ - Gemfile
82
+ - Gemfile.lock
83
+ - LICENSE.txt
84
+ - README.mkd
85
+ - Rakefile
86
+ - VERSION
87
+ - lib/core_ext/hash.rb
88
+ - lib/rack/session/redis_hash.rb
89
+ - lib/redis/marshal.rb
90
+ - lib/redis/native_hash.rb
91
+ - lib/redis/tracked_hash.rb
92
+ - redis-native_hash.gemspec
93
+ - spec/redis-hash_spec.rb
94
+ - spec/redis/redis_hash_spec.rb
95
+ - spec/spec_helper.rb
96
+ - spec/tracked_hash_spec.rb
97
+ - spec/user_defined/user_spec.rb
98
+ has_rdoc: true
99
+ homepage: http://github.com/carlzulauf/redis-native_hash
100
+ licenses:
101
+ - MIT
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ segments:
113
+ - 0
114
+ hash: 2729795531866201316
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 1.6.2
124
+ signing_key:
125
+ specification_version: 3
126
+ summary: ruby-hash-to-redis-hash mapping
127
+ test_files: []