dm-redis-adapter 0.0.11 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +12 -5
- data/Rakefile +5 -5
- data/lib/{dm_redis.rb → dm-redis-adapter/adapter.rb} +32 -14
- data/lib/dm-redis-adapter/spec/setup.rb +14 -0
- data/lib/dm-redis-adapter.rb +1 -0
- data/spec/dm_redis_spec.rb +11 -6
- data/spec/dm_redis_validations_spec.rb +48 -7
- data/spec/spec_helper.rb +1 -3
- metadata +29 -14
data/README.textile
CHANGED
@@ -2,23 +2,30 @@ h1. dm-redis-adapter
|
|
2
2
|
|
3
3
|
This is a <a href="http://datamapper.org">DataMapper</a> adapter for the <a href="http://github.com/antirez/redis/">Redis</a> key-value database.
|
4
4
|
|
5
|
-
Redis is a very fast key-value store with some interesting data structures added. You can have a key that is a SET, LIST, or
|
5
|
+
Redis is a very fast key-value store with some interesting data structures added. You can have a key that is a SET, LIST, STRING or HASH that is binary safe. Data structures like SET and LIST allow for even more interesting things. Redis is a fabulous and fast engine for data structures, and you can read more about it here: <a href="http://code.google.com/p/redis/">redis</a>. Redis is also a persistent data store, and can be used in large-scale environments with master-slave replication and consistent hashing on the client side.
|
6
6
|
|
7
7
|
<a href="http://datamapper.org">DataMapper</a> is a brilliant ORM that is based on the <a href="http://www.martinfowler.com/eaaCatalog/identityMap.html">IdentityMap</a> pattern. Usage of DataMapper resembles that of ActiveRecord, the popular ORM bundled with Ruby on Rails, but with some very important differences. A quote from the DM wiki: "One row in the database should equal one object reference. Pretty simple idea. Pretty profound impact." Having an identity map allows for very efficient queries to the database, as well as interesting forms of lazy loading of attributes or associations.
|
8
8
|
|
9
9
|
Marrying DataMapper to Redis allows for schema-less models, you can add fields at any time without having to create a migration. DataMapper also allows us to store non-native Redis types in the db, like Date fields.
|
10
10
|
|
11
|
+
h1. Upgrading
|
12
|
+
|
13
|
+
> v0.1 is not compatible with data created with previous versions! The new storage schema is designed to be more VM friendly. I won't be releasing a gem version of v0.1 until a stable version of redis that includes hash support has been released.
|
14
|
+
|
11
15
|
h1. Changelog
|
12
16
|
|
17
|
+
* v0.2.1 Fixes to sorting
|
18
|
+
* v0.1.1 Update to redis-rb v2.0.0
|
19
|
+
* v0.1 Update to store records as redis hash values
|
13
20
|
* v0.0.11 Updates to support newer versions of the redis client, support for JSON datatypes
|
14
21
|
|
15
22
|
h1. Install
|
16
23
|
|
17
24
|
Prerequisites:
|
18
25
|
* Redis:
|
19
|
-
** <a href="http://code.google.com/p/redis/">Redis,
|
26
|
+
** <a href="http://code.google.com/p/redis/">Redis, v2.0.x RC series</a>
|
20
27
|
* Gems:
|
21
|
-
** <a href="http://github.com/datamapper/dm-core/">dm-core</a>
|
28
|
+
** <a href="http://github.com/datamapper/dm-core/">dm-core</a> v1.0.0
|
22
29
|
|
23
30
|
Install the dm-redis adapter:
|
24
31
|
<pre>
|
@@ -89,5 +96,5 @@ h1. Badass contributors
|
|
89
96
|
|
90
97
|
* <a href="http://github.com/aeden">Anthony Eden (aeden)</a> Gem cleanup, update to jeweler
|
91
98
|
* <a href="http://github.com/sr">Simon Roset (sr)</a> Fixes for edge dm-core
|
92
|
-
* <a href="http://github.com/cehoffman">Chris Hoffman (cehoffman)</a> Fixes for Ruby 1.9, bundler for development deps
|
93
|
-
* <a href="http://github.com/bpo">brian p o'rourke (bpo)</a> Updates for newer versions of redis client and DM JSON type support
|
99
|
+
* <a href="http://github.com/cehoffman">Chris Hoffman (cehoffman)</a> Fixes for Ruby 1.9, bundler for development deps, fixes for sorting
|
100
|
+
* <a href="http://github.com/bpo">brian p o'rourke (bpo)</a> Updates for newer versions of redis client and DM JSON type support, move to hash storage
|
data/Rakefile
CHANGED
@@ -26,13 +26,13 @@ begin
|
|
26
26
|
gemspec.homepage = HOMEPAGE
|
27
27
|
gemspec.description = SUMMARY
|
28
28
|
gemspec.authors = AUTHORS
|
29
|
-
gemspec.add_dependency "dm-core", ">= 0.
|
30
|
-
gemspec.add_dependency "dm-types", ">= 0.
|
31
|
-
gemspec.add_dependency "redis"
|
29
|
+
gemspec.add_dependency "dm-core", ">= 1.0.0"
|
30
|
+
gemspec.add_dependency "dm-types", ">= 1.0.0"
|
31
|
+
gemspec.add_dependency "redis", ">= 2.0.3"
|
32
32
|
gemspec.files = %w(MIT-LICENSE README.textile Rakefile) + Dir.glob("{lib,spec}/**/*")
|
33
|
-
gemspec.has_rdoc =
|
33
|
+
gemspec.has_rdoc = false
|
34
34
|
gemspec.extra_rdoc_files = ["MIT-LICENSE"]
|
35
|
-
|
35
|
+
end
|
36
36
|
rescue LoadError
|
37
37
|
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
38
38
|
end
|
@@ -36,18 +36,19 @@ module DataMapper
|
|
36
36
|
# @api semipublic
|
37
37
|
def read(query)
|
38
38
|
records = records_for(query).each do |record|
|
39
|
+
record_data = @redis.hgetall( "#{query.model.to_s.downcase}:#{record[redis_key_for(query.model)]}" )
|
40
|
+
|
39
41
|
query.fields.each do |property|
|
40
42
|
next if query.model.key.include?(property)
|
41
43
|
|
44
|
+
name = property.name.to_s
|
45
|
+
value = record_data[name]
|
46
|
+
|
42
47
|
# Integers are stored as Strings in Redis. If there's a
|
43
48
|
# string coming out that should be an integer, convert it
|
44
49
|
# now. All other typecasting is handled by datamapper
|
45
50
|
# separately.
|
46
|
-
|
47
|
-
record[property.name.to_s] = property.typecast(@redis["#{query.model.to_s.downcase}:#{record[redis_key_for(query.model)]}:#{property.name}"])
|
48
|
-
else
|
49
|
-
record[property.name.to_s] = @redis["#{query.model.to_s.downcase}:#{record[redis_key_for(query.model)]}:#{property.name}"]
|
50
|
-
end
|
51
|
+
record[name] = [Integer, Date].include?(property.primitive) ? property.typecast( value ) : value
|
51
52
|
end
|
52
53
|
end
|
53
54
|
records = query.match_records(records)
|
@@ -88,9 +89,7 @@ module DataMapper
|
|
88
89
|
# @api semipublic
|
89
90
|
def delete(collection)
|
90
91
|
records_for(collection.query).each do |record|
|
91
|
-
collection.query.model.
|
92
|
-
@redis.del("#{collection.query.model.to_s.downcase}:#{record[redis_key_for(collection.query.model)]}:#{p.name}")
|
93
|
-
end
|
92
|
+
@redis.del("#{collection.query.model.to_s.downcase}:#{record[redis_key_for(collection.query.model)]}")
|
94
93
|
@redis.srem(key_set_for(collection.query.model), record[redis_key_for(collection.query.model)])
|
95
94
|
collection.query.model.properties.select {|p| p.index}.each do |p|
|
96
95
|
@redis.srem("#{collection.query.model.to_s.downcase}:#{p.name}:#{encode(record[p.name])}", record[redis_key_for(collection.query.model)])
|
@@ -116,11 +115,22 @@ module DataMapper
|
|
116
115
|
@redis.sadd("#{resource.model.to_s.downcase}:#{property.name}:#{encode(resource[property.name.to_s])}", resource.key)
|
117
116
|
end
|
118
117
|
|
119
|
-
|
120
|
-
|
118
|
+
properties_to_set = []
|
119
|
+
properties_to_del = []
|
120
|
+
|
121
|
+
fields = model.properties(self.name).select {|property| attributes.key?(property) }
|
122
|
+
fields.each do |property|
|
121
123
|
value = attributes[property]
|
122
|
-
|
124
|
+
if value.nil?
|
125
|
+
properties_to_del << property.name
|
126
|
+
else
|
127
|
+
properties_to_set << property.name << attributes[property]
|
128
|
+
end
|
123
129
|
end
|
130
|
+
|
131
|
+
hash_key = "#{resource.model.to_s.downcase}:#{resource.key.join}"
|
132
|
+
properties_to_del.each {|prop| @redis.hdel(hash_key, prop) }
|
133
|
+
@redis.hmset(hash_key, *properties_to_set) unless properties_to_set.empty?
|
124
134
|
end
|
125
135
|
end
|
126
136
|
|
@@ -147,9 +157,17 @@ module DataMapper
|
|
147
157
|
keys << {"#{redis_key_for(query.model)}" => k, "#{o.subject.name}" => o.value}
|
148
158
|
end
|
149
159
|
end
|
150
|
-
|
151
|
-
if query.limit
|
152
|
-
|
160
|
+
|
161
|
+
if query.order || query.limit
|
162
|
+
params = {}
|
163
|
+
params[:limit] = [query.offset, query.limit] if query.limit
|
164
|
+
|
165
|
+
if query.order
|
166
|
+
order = query.order.first
|
167
|
+
params[:order] = order.operator.to_s
|
168
|
+
end
|
169
|
+
|
170
|
+
@redis.sort(key_set_for(query.model), params).each do |val|
|
153
171
|
keys << {"#{redis_key_for(query.model)}" => val.to_i}
|
154
172
|
end
|
155
173
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'dm-redis-adapter/adapter'
|
data/spec/dm_redis_spec.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
require
|
2
|
-
require 'redis'
|
1
|
+
require 'spec_helper'
|
3
2
|
|
4
|
-
require 'dm-core
|
3
|
+
require 'dm-core'
|
4
|
+
require 'dm-core/spec/shared/adapter_spec'
|
5
|
+
require 'dm-redis-adapter/spec/setup'
|
6
|
+
|
7
|
+
ENV['ADAPTER'] = 'redis'
|
8
|
+
ENV['ADAPTER_SUPPORTS'] = 'all'
|
5
9
|
|
6
10
|
describe DataMapper::Adapters::RedisAdapter do
|
7
11
|
before(:all) do
|
@@ -9,12 +13,13 @@ describe DataMapper::Adapters::RedisAdapter do
|
|
9
13
|
:adapter => "redis",
|
10
14
|
:db => 15
|
11
15
|
})
|
16
|
+
@repository = DataMapper.repository(@adapter.name)
|
12
17
|
end
|
13
18
|
|
19
|
+
it_should_behave_like 'An Adapter'
|
20
|
+
|
14
21
|
after(:all) do
|
15
22
|
redis = Redis.new(:db => 15)
|
16
23
|
redis.flushdb
|
17
24
|
end
|
18
|
-
|
19
|
-
it_should_behave_like 'An Adapter'
|
20
|
-
end
|
25
|
+
end
|
@@ -1,22 +1,21 @@
|
|
1
1
|
require File.expand_path("../spec_helper", __FILE__)
|
2
|
-
require 'redis'
|
3
|
-
require 'rubygems'
|
4
2
|
require 'dm-validations'
|
5
3
|
require 'dm-types'
|
6
|
-
require
|
4
|
+
require 'logger'
|
7
5
|
|
8
6
|
describe DataMapper::Adapters::RedisAdapter do
|
9
7
|
before(:all) do
|
10
8
|
@adapter = DataMapper.setup(:default, {
|
11
9
|
:adapter => "redis",
|
12
|
-
:db => 15
|
10
|
+
:db => 15,
|
11
|
+
# :logger => Logger.new(STDOUT)
|
13
12
|
})
|
14
13
|
end
|
15
14
|
|
16
15
|
it "should validate unique entries that are indexed" do
|
17
16
|
class Crumblecake
|
18
17
|
include DataMapper::Resource
|
19
|
-
|
18
|
+
validates_uniqueness_of :flavor
|
20
19
|
|
21
20
|
property :id, Serial
|
22
21
|
property :flavor, String, :index => true
|
@@ -33,7 +32,7 @@ describe DataMapper::Adapters::RedisAdapter do
|
|
33
32
|
|
34
33
|
property :id, Serial
|
35
34
|
property :name, String
|
36
|
-
property :env, DataMapper::
|
35
|
+
property :env, DataMapper::Property::Json, :default => lambda { {} }
|
37
36
|
end
|
38
37
|
end
|
39
38
|
|
@@ -52,8 +51,50 @@ describe DataMapper::Adapters::RedisAdapter do
|
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
54
|
+
it "should allow me to delete properties" do
|
55
|
+
class User
|
56
|
+
include DataMapper::Resource
|
57
|
+
|
58
|
+
property :id, Serial
|
59
|
+
property :name, String
|
60
|
+
end
|
61
|
+
|
62
|
+
u = User.create :name => "bpo"
|
63
|
+
u.reload.name.should == "bpo"
|
64
|
+
u.name = nil
|
65
|
+
u.save
|
66
|
+
u.reload.name.should == nil
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should store Date fields" do
|
70
|
+
class Post
|
71
|
+
include DataMapper::Resource
|
72
|
+
|
73
|
+
property :id, Serial
|
74
|
+
property :posted_at, Date
|
75
|
+
end
|
76
|
+
|
77
|
+
Post.create :posted_at => Date.today
|
78
|
+
Post.first.posted_at.should be_a(Date)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should get the first and last model inserted" do
|
82
|
+
class GangMember
|
83
|
+
include DataMapper::Resource
|
84
|
+
|
85
|
+
property :id, Serial
|
86
|
+
property :nickname, String
|
87
|
+
end
|
88
|
+
|
89
|
+
joey = GangMember.create(:nickname => "Joey 'two-times'")
|
90
|
+
bobby = GangMember.create(:nickname => "Bobby 'three-fingers'")
|
91
|
+
|
92
|
+
GangMember.first.should == joey
|
93
|
+
GangMember.last.should == bobby
|
94
|
+
end
|
95
|
+
|
55
96
|
after(:all) do
|
56
97
|
redis = Redis.new(:db => 15)
|
57
98
|
redis.flushdb
|
58
99
|
end
|
59
|
-
end
|
100
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-redis-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 1
|
10
|
+
version: 0.2.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Dan Herrera
|
@@ -14,47 +15,55 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-07-12 00:00:00 -07:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: dm-core
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
27
30
|
segments:
|
31
|
+
- 1
|
28
32
|
- 0
|
29
|
-
-
|
30
|
-
|
31
|
-
version: 0.10.2
|
33
|
+
- 0
|
34
|
+
version: 1.0.0
|
32
35
|
type: :runtime
|
33
36
|
version_requirements: *id001
|
34
37
|
- !ruby/object:Gem::Dependency
|
35
38
|
name: dm-types
|
36
39
|
prerelease: false
|
37
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
38
42
|
requirements:
|
39
43
|
- - ">="
|
40
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 23
|
41
46
|
segments:
|
47
|
+
- 1
|
42
48
|
- 0
|
43
|
-
-
|
44
|
-
|
45
|
-
version: 0.10.2
|
49
|
+
- 0
|
50
|
+
version: 1.0.0
|
46
51
|
type: :runtime
|
47
52
|
version_requirements: *id002
|
48
53
|
- !ruby/object:Gem::Dependency
|
49
54
|
name: redis
|
50
55
|
prerelease: false
|
51
56
|
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
52
58
|
requirements:
|
53
59
|
- - ">="
|
54
60
|
- !ruby/object:Gem::Version
|
61
|
+
hash: 9
|
55
62
|
segments:
|
63
|
+
- 2
|
56
64
|
- 0
|
57
|
-
|
65
|
+
- 3
|
66
|
+
version: 2.0.3
|
58
67
|
type: :runtime
|
59
68
|
version_requirements: *id003
|
60
69
|
description: DataMapper adapter for the Redis key-value database
|
@@ -69,7 +78,9 @@ files:
|
|
69
78
|
- MIT-LICENSE
|
70
79
|
- README.textile
|
71
80
|
- Rakefile
|
72
|
-
- lib/
|
81
|
+
- lib/dm-redis-adapter.rb
|
82
|
+
- lib/dm-redis-adapter/adapter.rb
|
83
|
+
- lib/dm-redis-adapter/spec/setup.rb
|
73
84
|
- spec/dm_redis_spec.rb
|
74
85
|
- spec/dm_redis_validations_spec.rb
|
75
86
|
- spec/spec_helper.rb
|
@@ -77,29 +88,33 @@ has_rdoc: true
|
|
77
88
|
homepage: http://github.com/whoahbot/dm-redis-adapter
|
78
89
|
licenses: []
|
79
90
|
|
80
|
-
post_install_message:
|
91
|
+
post_install_message: "\n !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n (!!) U P G R A D I N G (!!)\n\n WAAAAAAAAAAAAAAAAAAAAAAAIT!\n Earlier versions of dm-redis-adapter\n use a different method of storing properties\n which means that this version of dm-redis-adapter\n won't read them properly.\n\n !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n "
|
81
92
|
rdoc_options:
|
82
93
|
- --charset=UTF-8
|
83
94
|
require_paths:
|
84
95
|
- lib
|
85
96
|
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
86
98
|
requirements:
|
87
99
|
- - ">="
|
88
100
|
- !ruby/object:Gem::Version
|
101
|
+
hash: 3
|
89
102
|
segments:
|
90
103
|
- 0
|
91
104
|
version: "0"
|
92
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
93
107
|
requirements:
|
94
108
|
- - ">="
|
95
109
|
- !ruby/object:Gem::Version
|
110
|
+
hash: 3
|
96
111
|
segments:
|
97
112
|
- 0
|
98
113
|
version: "0"
|
99
114
|
requirements: []
|
100
115
|
|
101
116
|
rubyforge_project:
|
102
|
-
rubygems_version: 1.3.
|
117
|
+
rubygems_version: 1.3.7
|
103
118
|
signing_key:
|
104
119
|
specification_version: 3
|
105
120
|
summary: DataMapper adapter for the Redis key-value database
|