whoahbot-dm-redis 0.0.2
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/MIT-LICENSE +22 -0
- data/README.textile +18 -0
- data/Rakefile +55 -0
- data/lib/redis_adapter.rb +170 -0
- data/lib/rubyredis.rb +237 -0
- data/spec/dm_redis_adapter_spec.rb +21 -0
- data/spec/spec_helper.rb +4 -0
- metadata +78 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2009 Dan Herrera
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
h1. DM-Redis
|
2
|
+
|
3
|
+
This is an experimental datamapper adapter for the <a href="http://code.google.com/p/redis/wiki/README">Redis</a> key-value database.
|
4
|
+
|
5
|
+
Please be aware that this is very alpha quality software! It is not recommended for production use yet. If you find a bug, or are using dm-redis, please let me know.
|
6
|
+
|
7
|
+
h1. TODO
|
8
|
+
|
9
|
+
Refactoring +records_for+ to filter down the set of records returned.
|
10
|
+
|
11
|
+
h1. Install
|
12
|
+
|
13
|
+
Prerequisites:
|
14
|
+
* Redis, git version:
|
15
|
+
** <a href="http://github.com/antirez/redis/">Redis, git version</a>
|
16
|
+
* Gems:
|
17
|
+
** <a href="http://github.com/datamapper/extlib">extlib</a>, dependency for dm-core
|
18
|
+
** <a href="http://github.com/datamapper/dm-core/tree/next">dm-core</a> next branch
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rubygems/specification'
|
4
|
+
require 'date'
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
|
7
|
+
GEM = 'dm-redis'
|
8
|
+
GEM_NAME = 'dm-redis'
|
9
|
+
GEM_VERSION = '0.0.2'
|
10
|
+
AUTHORS = ['Dan Herrera']
|
11
|
+
EMAIL = "whoahbot@gmail.com"
|
12
|
+
HOMEPAGE = "http://github.com/whoahbot/dm-redis"
|
13
|
+
SUMMARY = "DataMapper adapter for the Redis key-value database"
|
14
|
+
|
15
|
+
spec = Gem::Specification.new do |s|
|
16
|
+
s.name = GEM
|
17
|
+
s.version = GEM_VERSION
|
18
|
+
s.platform = Gem::Platform::RUBY
|
19
|
+
s.has_rdoc = true
|
20
|
+
s.extra_rdoc_files = ["MIT-LICENSE"]
|
21
|
+
s.summary = SUMMARY
|
22
|
+
s.description = s.summary
|
23
|
+
s.authors = AUTHORS
|
24
|
+
s.email = EMAIL
|
25
|
+
s.homepage = HOMEPAGE
|
26
|
+
s.add_dependency "rspec"
|
27
|
+
s.add_dependency "dm-core", "0.10.0"
|
28
|
+
s.require_path = 'lib'
|
29
|
+
s.autorequire = GEM
|
30
|
+
s.files = %w(MIT-LICENSE README.textile Rakefile) + Dir.glob("{lib,spec}/**/*")
|
31
|
+
end
|
32
|
+
|
33
|
+
task :default => :spec
|
34
|
+
|
35
|
+
desc "Run specs"
|
36
|
+
Spec::Rake::SpecTask.new do |t|
|
37
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
38
|
+
t.spec_opts = %w(-fs --color)
|
39
|
+
end
|
40
|
+
|
41
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
42
|
+
pkg.gem_spec = spec
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "install the gem locally"
|
46
|
+
task :install => [:package] do
|
47
|
+
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "create a gemspec file"
|
51
|
+
task :make_spec do
|
52
|
+
File.open("#{GEM}.gemspec", "w") do |file|
|
53
|
+
file.puts spec.to_ruby
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'rubyredis'))
|
2
|
+
|
3
|
+
module DataMapper
|
4
|
+
module Adapters
|
5
|
+
Extlib::Inflection.word 'redis'
|
6
|
+
|
7
|
+
class RedisAdapter < AbstractAdapter
|
8
|
+
##
|
9
|
+
# Used by DataMapper to put records into the redis data-store: "INSERT" in SQL-speak.
|
10
|
+
# It takes an array of the resources (model instances) to be saved. Resources
|
11
|
+
# each have a key that can be used to quickly look them up later without
|
12
|
+
# searching.
|
13
|
+
#
|
14
|
+
# @param [Enumerable(Resource)] resources
|
15
|
+
# The set of resources (model instances)
|
16
|
+
#
|
17
|
+
# @api semipublic
|
18
|
+
def create(resources)
|
19
|
+
resources.each do |resource|
|
20
|
+
initialize_identity_field(resource, @redis.incr("#{resource.model}:#{redis_key_for(resource.model)}:serial"))
|
21
|
+
@redis.set_add("#{resource.model}:#{redis_key_for(resource.model)}:all", resource.key)
|
22
|
+
end
|
23
|
+
|
24
|
+
update_attributes(resources)
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Looks up one record or a collection of records from the data-store:
|
29
|
+
# "SELECT" in SQL.
|
30
|
+
#
|
31
|
+
# @param [Query] query
|
32
|
+
# The query to be used to seach for the resources
|
33
|
+
#
|
34
|
+
# @return [Array]
|
35
|
+
# An Array of Hashes containing the key-value pairs for
|
36
|
+
# each record
|
37
|
+
#
|
38
|
+
# @api semipublic
|
39
|
+
def read(query)
|
40
|
+
records = records_for(query).each do |record|
|
41
|
+
query.fields.each do |property|
|
42
|
+
next if query.model.key.include?(property.name)
|
43
|
+
record[property.name.to_s] = property.typecast(@redis["#{query.model}:#{record[redis_key_for(query.model)]}:#{property.name}"])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
records = query.match_records(records)
|
48
|
+
records = query.sort_records(records)
|
49
|
+
records = query.limit_records(records)
|
50
|
+
records
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Used by DataMapper to update the attributes on existing records in the redis
|
55
|
+
# data-store: "UPDATE" in SQL-speak. It takes a hash of the attributes
|
56
|
+
# to update with, as well as a collection object that specifies which resources
|
57
|
+
# should be updated.
|
58
|
+
#
|
59
|
+
# @param [Hash] attributes
|
60
|
+
# A set of key-value pairs of the attributes to update the resources with.
|
61
|
+
# @param [DataMapper::Collection] collection
|
62
|
+
# The query that should be used to find the resource(s) to update.
|
63
|
+
#
|
64
|
+
# @api semipublic
|
65
|
+
def update(attributes, collection)
|
66
|
+
attributes = attributes_as_fields(attributes)
|
67
|
+
read(collection.query).each { |r| r.update(attributes) }
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Destroys all the records matching the given query. "DELETE" in SQL.
|
72
|
+
#
|
73
|
+
# @param [DataMapper::Collection] collection
|
74
|
+
# The query used to locate the resources to be deleted.
|
75
|
+
#
|
76
|
+
# @return [Array]
|
77
|
+
# An Array of Hashes containing the key-value pairs for
|
78
|
+
# each record
|
79
|
+
#
|
80
|
+
# @api semipublic
|
81
|
+
def delete(collection)
|
82
|
+
collection.query.filter_records(records_for(collection.query)).each do |record|
|
83
|
+
collection.query.model.properties.each do |p|
|
84
|
+
@redis.delete("#{collection.query.model}:#{record[redis_key_for(collection.query.model)]}:#{p.name}")
|
85
|
+
end
|
86
|
+
@redis.set_delete("#{collection.query.model}:#{redis_key_for(collection.query.model)}:all", record[redis_key_for(collection.query.model)])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
##
|
93
|
+
# Creates a string representation for the keys in a given model
|
94
|
+
#
|
95
|
+
# @param [DataMapper::Model] model
|
96
|
+
# The query used to locate the resources to be deleted.
|
97
|
+
#
|
98
|
+
# @return [Array]
|
99
|
+
# An Array of Hashes containing the key-value pairs for
|
100
|
+
# each record
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
def redis_key_for(model)
|
104
|
+
model.key.collect {|k| k.name}.join(":")
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# Saves each key value pair to the redis data store
|
109
|
+
#
|
110
|
+
# @param [Array] resources
|
111
|
+
# An array of resources to save
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
def update_attributes(resources)
|
115
|
+
resources.each do |resource|
|
116
|
+
resource.attributes.each do |property, value|
|
117
|
+
@redis["#{resource.model}:#{resource.key}:#{property}"] = value unless value.nil?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Retrieves records for a particular model.
|
124
|
+
#
|
125
|
+
# @param [DataMapper::Query] query
|
126
|
+
# The query used to locate the resources
|
127
|
+
#
|
128
|
+
# @return [Array]
|
129
|
+
# An array of hashes of all of the records for a particular model
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
def records_for(query)
|
133
|
+
keys = []
|
134
|
+
query.conditions.operands.select {|o| o.is_a?(Conditions::EqualToComparison) && query.model.key.include?(o.property)}.each do |o|
|
135
|
+
if @redis.set_member?("#{query.model}:#{redis_key_for(query.model)}:all", o.value)
|
136
|
+
keys << {"#{redis_key_for(query.model)}" => o.value}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# TODO: Implement other conditions to filter down the records retrieved
|
141
|
+
# Keys are empty, fall back and load all the values for this model
|
142
|
+
if keys.empty?
|
143
|
+
@redis.set_members("#{query.model}:#{redis_key_for(query.model)}:all").each do |val|
|
144
|
+
keys << {"#{redis_key_for(query.model)}" => val.to_i}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
keys
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Make a new instance of the adapter. The @redis ivar is the 'data-store'
|
153
|
+
# for this adapter.
|
154
|
+
#
|
155
|
+
# @param [String, Symbol] name
|
156
|
+
# The name of the Repository using this adapter.
|
157
|
+
# @param [String, Hash] uri_or_options
|
158
|
+
# The connection uri string, or a hash of options to set up
|
159
|
+
# the adapter
|
160
|
+
#
|
161
|
+
# @api semipublic
|
162
|
+
def initialize(name, uri_or_options)
|
163
|
+
super
|
164
|
+
@redis = RedisClient.new(@options)
|
165
|
+
end
|
166
|
+
end # class RedisAdapter
|
167
|
+
|
168
|
+
const_added(:RedisAdapter)
|
169
|
+
end # module Adapters
|
170
|
+
end # module DataMapper
|
data/lib/rubyredis.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
# RubyRedis is an alternative implementatin of Ruby client library written
|
2
|
+
# by Salvatore Sanfilippo.
|
3
|
+
#
|
4
|
+
# The aim of this library is to create an alternative client library that is
|
5
|
+
# much simpler and does not implement every command explicitly but uses
|
6
|
+
# method_missing instead.
|
7
|
+
|
8
|
+
require 'socket'
|
9
|
+
require 'set'
|
10
|
+
|
11
|
+
begin
|
12
|
+
if (RUBY_VERSION >= '1.9')
|
13
|
+
require 'timeout'
|
14
|
+
RedisTimer = Timeout
|
15
|
+
else
|
16
|
+
require 'system_timer'
|
17
|
+
RedisTimer = SystemTimer
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
RedisTimer = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
class RedisClient
|
24
|
+
BulkCommands = {
|
25
|
+
"set"=>true, "setnx"=>true, "rpush"=>true, "lpush"=>true, "lset"=>true,
|
26
|
+
"lrem"=>true, "sadd"=>true, "srem"=>true, "sismember"=>true,
|
27
|
+
"echo"=>true, "getset"=>true, "smove"=>true
|
28
|
+
}
|
29
|
+
|
30
|
+
ConvertToBool = lambda{|r| r == 0 ? false : r}
|
31
|
+
|
32
|
+
ReplyProcessor = {
|
33
|
+
"exists" => ConvertToBool,
|
34
|
+
"sismember"=> ConvertToBool,
|
35
|
+
"sadd"=> ConvertToBool,
|
36
|
+
"srem"=> ConvertToBool,
|
37
|
+
"smove"=> ConvertToBool,
|
38
|
+
"move"=> ConvertToBool,
|
39
|
+
"setnx"=> ConvertToBool,
|
40
|
+
"del"=> ConvertToBool,
|
41
|
+
"renamenx"=> ConvertToBool,
|
42
|
+
"expire"=> ConvertToBool,
|
43
|
+
"keys" => lambda{|r| r.split(" ")},
|
44
|
+
"info" => lambda{|r|
|
45
|
+
info = {}
|
46
|
+
r.each_line {|kv|
|
47
|
+
k,v = kv.split(":",2).map{|x| x.chomp}
|
48
|
+
info[k.to_sym] = v
|
49
|
+
}
|
50
|
+
info
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
Aliases = {
|
55
|
+
"flush_db" => "flushdb",
|
56
|
+
"flush_all" => "flushall",
|
57
|
+
"last_save" => "lastsave",
|
58
|
+
"key?" => "exists",
|
59
|
+
"delete" => "del",
|
60
|
+
"randkey" => "randomkey",
|
61
|
+
"list_length" => "llen",
|
62
|
+
"push_tail" => "rpush",
|
63
|
+
"push_head" => "lpush",
|
64
|
+
"pop_tail" => "rpop",
|
65
|
+
"pop_head" => "lpop",
|
66
|
+
"list_set" => "lset",
|
67
|
+
"list_range" => "lrange",
|
68
|
+
"list_trim" => "ltrim",
|
69
|
+
"list_index" => "lindex",
|
70
|
+
"list_rm" => "lrem",
|
71
|
+
"set_add" => "sadd",
|
72
|
+
"set_delete" => "srem",
|
73
|
+
"set_count" => "scard",
|
74
|
+
"set_member?" => "sismember",
|
75
|
+
"set_members" => "smembers",
|
76
|
+
"set_intersect" => "sinter",
|
77
|
+
"set_intersect_store" => "sinterstore",
|
78
|
+
"set_inter_store" => "sinterstore",
|
79
|
+
"set_union" => "sunion",
|
80
|
+
"set_union_store" => "sunionstore",
|
81
|
+
"set_diff" => "sdiff",
|
82
|
+
"set_diff_store" => "sdiffstore",
|
83
|
+
"set_move" => "smove",
|
84
|
+
"set_unless_exists" => "setnx",
|
85
|
+
"rename_unless_exists" => "renamenx"
|
86
|
+
}
|
87
|
+
|
88
|
+
def initialize(opts={})
|
89
|
+
@host = opts[:host] || '127.0.0.1'
|
90
|
+
@port = opts[:port] || 6379
|
91
|
+
@db = opts[:db] || 0
|
92
|
+
@timeout = opts[:timeout] || 0
|
93
|
+
connect_to_server
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
"Redis Client connected to #{@host}:#{@port} against DB #{@db}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def connect_to_server
|
101
|
+
@sock = connect_to(@host,@port,@timeout == 0 ? nil : @timeout)
|
102
|
+
call_command(["select",@db]) if @db != 0
|
103
|
+
end
|
104
|
+
|
105
|
+
def connect_to(host, port, timeout=nil)
|
106
|
+
# We support connect() timeout only if system_timer is availabe
|
107
|
+
# or if we are running against Ruby >= 1.9
|
108
|
+
# Timeout reading from the socket instead will be supported anyway.
|
109
|
+
if @timeout != 0 and RedisTimer
|
110
|
+
begin
|
111
|
+
sock = TCPSocket.new(host, port, 0)
|
112
|
+
rescue Timeout::Error
|
113
|
+
@sock = nil
|
114
|
+
raise Timeout::Error, "Timeout connecting to the server"
|
115
|
+
end
|
116
|
+
else
|
117
|
+
sock = TCPSocket.new(host, port, 0)
|
118
|
+
end
|
119
|
+
|
120
|
+
# If the timeout is set we set the low level socket options in order
|
121
|
+
# to make sure a blocking read will return after the specified number
|
122
|
+
# of seconds. This hack is from memcached ruby client.
|
123
|
+
if timeout
|
124
|
+
secs = Integer(timeout)
|
125
|
+
usecs = Integer((timeout - secs) * 1_000_000)
|
126
|
+
optval = [secs, usecs].pack("l_2")
|
127
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
128
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
129
|
+
end
|
130
|
+
sock
|
131
|
+
end
|
132
|
+
|
133
|
+
def method_missing(*argv)
|
134
|
+
call_command(argv)
|
135
|
+
end
|
136
|
+
|
137
|
+
def call_command(argv)
|
138
|
+
# this wrapper to raw_call_command handle reconnection on socket
|
139
|
+
# error. We try to reconnect just one time, otherwise let the error
|
140
|
+
# araise.
|
141
|
+
connect_to_server if !@sock
|
142
|
+
begin
|
143
|
+
raw_call_command(argv)
|
144
|
+
rescue Errno::ECONNRESET
|
145
|
+
@sock.close
|
146
|
+
connect_to_server
|
147
|
+
raw_call_command(argv)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def raw_call_command(argv)
|
152
|
+
bulk = nil
|
153
|
+
argv[0] = argv[0].to_s.downcase
|
154
|
+
argv[0] = Aliases[argv[0]] if Aliases[argv[0]]
|
155
|
+
if BulkCommands[argv[0]]
|
156
|
+
bulk = argv[-1].to_s
|
157
|
+
argv[-1] = bulk.length
|
158
|
+
end
|
159
|
+
@sock.write(argv.join(" ")+"\r\n")
|
160
|
+
@sock.write(bulk+"\r\n") if bulk
|
161
|
+
|
162
|
+
# Post process the reply if needed
|
163
|
+
processor = ReplyProcessor[argv[0]]
|
164
|
+
processor ? processor.call(read_reply) : read_reply
|
165
|
+
end
|
166
|
+
|
167
|
+
def select(*args)
|
168
|
+
raise "SELECT not allowed, use the :db option when creating the object"
|
169
|
+
end
|
170
|
+
|
171
|
+
def [](key)
|
172
|
+
get(key)
|
173
|
+
end
|
174
|
+
|
175
|
+
def []=(key,value)
|
176
|
+
set(key,value)
|
177
|
+
end
|
178
|
+
|
179
|
+
def sort(key, opts={})
|
180
|
+
cmd = []
|
181
|
+
cmd << "SORT #{key}"
|
182
|
+
cmd << "BY #{opts[:by]}" if opts[:by]
|
183
|
+
cmd << "GET #{[opts[:get]].flatten * ' GET '}" if opts[:get]
|
184
|
+
cmd << "#{opts[:order]}" if opts[:order]
|
185
|
+
cmd << "LIMIT #{opts[:limit].join(' ')}" if opts[:limit]
|
186
|
+
call_command(cmd)
|
187
|
+
end
|
188
|
+
|
189
|
+
def incr(key,increment=nil)
|
190
|
+
call_command(increment ? ["incrby",key,increment] : ["incr",key])
|
191
|
+
end
|
192
|
+
|
193
|
+
def decr(key,decrement=nil)
|
194
|
+
call_command(decrement ? ["decrby",key,decrement] : ["decr",key])
|
195
|
+
end
|
196
|
+
|
197
|
+
def read_reply
|
198
|
+
# We read the first byte using read() mainly because gets() is
|
199
|
+
# immune to raw socket timeouts.
|
200
|
+
begin
|
201
|
+
rtype = @sock.read(1)
|
202
|
+
rescue Errno::EAGAIN
|
203
|
+
# We want to make sure it reconnects on the next command after the
|
204
|
+
# timeout. Otherwise the server may reply in the meantime leaving
|
205
|
+
# the protocol in a desync status.
|
206
|
+
@sock = nil
|
207
|
+
raise Errno::EAGAIN, "Timeout reading from the socket"
|
208
|
+
end
|
209
|
+
|
210
|
+
raise Errno::ECONNRESET,"Connection lost" if !rtype
|
211
|
+
line = @sock.gets
|
212
|
+
case rtype
|
213
|
+
when "-"
|
214
|
+
raise "-"+line.strip
|
215
|
+
when "+"
|
216
|
+
line.strip
|
217
|
+
when ":"
|
218
|
+
line.to_i
|
219
|
+
when "$"
|
220
|
+
bulklen = line.to_i
|
221
|
+
return nil if bulklen == -1
|
222
|
+
data = @sock.read(bulklen)
|
223
|
+
@sock.read(2) # CRLF
|
224
|
+
data
|
225
|
+
when "*"
|
226
|
+
objects = line.to_i
|
227
|
+
return nil if bulklen == -1
|
228
|
+
res = []
|
229
|
+
objects.times {
|
230
|
+
res << read_reply
|
231
|
+
}
|
232
|
+
res
|
233
|
+
else
|
234
|
+
raise "Protocol error, got '#{rtype}' as initial reply byte"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib/redis_adapter.rb'))
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib/rubyredis'))
|
4
|
+
|
5
|
+
require 'dm-core/spec/adapter_shared_spec'
|
6
|
+
|
7
|
+
describe DataMapper::Adapters::RedisAdapter do
|
8
|
+
before(:all) do
|
9
|
+
@adapter = DataMapper.setup(:default, {
|
10
|
+
:adapter => "redis",
|
11
|
+
:db => 15
|
12
|
+
})
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:all) do
|
16
|
+
redis = RedisClient.new(:db => 15)
|
17
|
+
redis.flushdb
|
18
|
+
end
|
19
|
+
|
20
|
+
it_should_behave_like 'An Adapter'
|
21
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: whoahbot-dm-redis
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Herrera
|
8
|
+
autorequire: dm-redis
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-24 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: dm-core
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.10.0
|
34
|
+
version:
|
35
|
+
description: DataMapper adapter for the Redis key-value database
|
36
|
+
email: whoahbot@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- MIT-LICENSE
|
43
|
+
files:
|
44
|
+
- MIT-LICENSE
|
45
|
+
- README.textile
|
46
|
+
- Rakefile
|
47
|
+
- lib/redis_adapter.rb
|
48
|
+
- lib/rubyredis.rb
|
49
|
+
- spec/dm_redis_adapter_spec.rb
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
has_rdoc: false
|
52
|
+
homepage: http://github.com/whoahbot/dm-redis
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.2.0
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: DataMapper adapter for the Redis key-value database
|
77
|
+
test_files: []
|
78
|
+
|