sinatra-redis 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ Sinatra Redis Extension
2
+ ========================
3
+
4
+ Extends [Sinatra](http://www.sinatrarb.com/) with an extension method for
5
+ dealing with redis databases using the rubyredis.rb client library that
6
+ comes with the redis source (pre-packaged with this library for convenience).
7
+
8
+ Install the `sinatra-redis` with rip. No gem support yet.
9
+
10
+ $ rip install git://github.com/bmizerany/sinatra-redis.git
11
+ $ vim sinatra-using-redis.rb
12
+
13
+ require 'sinatra'
14
+ require 'sinatra/redis'
15
+
16
+ # Establish the database connection; or, omit this and use the REDIS_URL
17
+ # environment variable as the connection string; or, default to redis://locahost:6379/0
18
+ #
19
+ # NOTE: The database is the integer in the path
20
+ # set :redis, 'redis://some-remote-server:1234/5'
21
+
22
+ # At this point, you can access the Redis object using the "redis" object:
23
+ puts redis.delete "foos"
24
+ puts redis.rpush "foos", "redis"
25
+ puts redis.rpush "foos", "is"
26
+ puts redis.rpush "foos", "sweet!"
27
+
28
+ # access redis within the context of an HTTP request
29
+ get '/foos' do
30
+ @foos = redis.lrange("foos", 0, -1) # Array
31
+ @foos.inspect
32
+ end
33
+
34
+ $ ruby sinatra-using-redis.rb
35
+
36
+ ### Redis Reference Material
37
+
38
+ * The [Redis Wiki](http://code.google.com/p/redis/)
39
+
40
+ * The [Redis Command Reference](http://code.google.com/p/redis/wiki/CommandReference)
41
+
42
+ * The [Redis Source](http://github.com/antirez/redis)
43
+
44
+ * Ezra's Mountain West Ruby Conf '09 [Talk](http://mwrc2009.confreaks.com/13-mar-2009-19-24-redis-key-value-nirvana-ezra-zygmuntowicz.html)
45
+
46
+ ### NOTE about the rip-off
47
+
48
+ This Code and README.md is a heavy adaption of [rtomayko's sinatra-sequel](http://github.com/rtomayko/sinatra-sequel/)
@@ -0,0 +1,38 @@
1
+ require 'uri'
2
+ require 'sinatra/redis/rubyredis'
3
+
4
+ module Sinatra
5
+ module RedisHelper
6
+ def redis
7
+ options.redis
8
+ end
9
+ end
10
+
11
+ module RedisExtension
12
+ def redis=(url)
13
+ @redis = nil
14
+ set :redis_url, url
15
+ redis
16
+ end
17
+
18
+ def redis
19
+ url = URI(redis_url)
20
+ @redis ||=
21
+ RedisClient.new(
22
+ :host => url.host,
23
+ :port => url.port,
24
+ :db => url.path[1..-1],
25
+ :pass => url.password
26
+ )
27
+ end
28
+
29
+ protected
30
+
31
+ def self.registered(app)
32
+ app.set :redis_url, ENV['REDIS_URL'] || "redis://127.0.0.1:6379/0"
33
+ app.helpers RedisHelper
34
+ end
35
+ end
36
+
37
+ register RedisExtension
38
+ end
@@ -0,0 +1,243 @@
1
+ # NOTE: This library is packaged with sinatra-redis for
2
+ # convenence. It's been copied from the redis repo.
3
+
4
+ # RubyRedis is an alternative implementatin of Ruby client library written
5
+ # by Salvatore Sanfilippo.
6
+ #
7
+ # The aim of this library is to create an alternative client library that is
8
+ # much simpler and does not implement every command explicitly but uses
9
+ # method_missing instead.
10
+
11
+ require 'socket'
12
+ require 'set'
13
+
14
+ begin
15
+ if (RUBY_VERSION >= '1.9')
16
+ require 'timeout'
17
+ RedisTimer = Timeout
18
+ else
19
+ require 'system_timer'
20
+ RedisTimer = SystemTimer
21
+ end
22
+ rescue LoadError
23
+ RedisTimer = nil
24
+ end
25
+
26
+ class RedisClient
27
+ BulkCommands = {
28
+ "set"=>true, "setnx"=>true, "rpush"=>true, "lpush"=>true, "lset"=>true,
29
+ "lrem"=>true, "sadd"=>true, "srem"=>true, "sismember"=>true,
30
+ "echo"=>true, "getset"=>true, "smove"=>true
31
+ }
32
+
33
+ ConvertToBool = lambda{|r| r == 0 ? false : r}
34
+
35
+ ReplyProcessor = {
36
+ "exists" => ConvertToBool,
37
+ "sismember"=> ConvertToBool,
38
+ "sadd"=> ConvertToBool,
39
+ "srem"=> ConvertToBool,
40
+ "smove"=> ConvertToBool,
41
+ "move"=> ConvertToBool,
42
+ "setnx"=> ConvertToBool,
43
+ "del"=> ConvertToBool,
44
+ "renamenx"=> ConvertToBool,
45
+ "expire"=> ConvertToBool,
46
+ "keys" => lambda{|r| r.split(" ")},
47
+ "info" => lambda{|r|
48
+ info = {}
49
+ r.each_line {|kv|
50
+ k,v = kv.split(":",2).map{|x| x.chomp}
51
+ info[k.to_sym] = v
52
+ }
53
+ info
54
+ }
55
+ }
56
+
57
+ Aliases = {
58
+ "flush_db" => "flushdb",
59
+ "flush_all" => "flushall",
60
+ "last_save" => "lastsave",
61
+ "key?" => "exists",
62
+ "delete" => "del",
63
+ "randkey" => "randomkey",
64
+ "list_length" => "llen",
65
+ "push_tail" => "rpush",
66
+ "push_head" => "lpush",
67
+ "pop_tail" => "rpop",
68
+ "pop_head" => "lpop",
69
+ "list_set" => "lset",
70
+ "list_range" => "lrange",
71
+ "list_trim" => "ltrim",
72
+ "list_index" => "lindex",
73
+ "list_rm" => "lrem",
74
+ "set_add" => "sadd",
75
+ "set_delete" => "srem",
76
+ "set_count" => "scard",
77
+ "set_member?" => "sismember",
78
+ "set_members" => "smembers",
79
+ "set_intersect" => "sinter",
80
+ "set_intersect_store" => "sinterstore",
81
+ "set_inter_store" => "sinterstore",
82
+ "set_union" => "sunion",
83
+ "set_union_store" => "sunionstore",
84
+ "set_diff" => "sdiff",
85
+ "set_diff_store" => "sdiffstore",
86
+ "set_move" => "smove",
87
+ "set_unless_exists" => "setnx",
88
+ "rename_unless_exists" => "renamenx"
89
+ }
90
+
91
+ def initialize(opts={})
92
+ @host = opts[:host] || '127.0.0.1'
93
+ @port = opts[:port] || 6379
94
+ @db = opts[:db] || 0
95
+ @timeout = opts[:timeout] || 0
96
+ @pass = opts[:pass] || ""
97
+ connect_to_server
98
+ end
99
+
100
+ def to_s
101
+ "Redis Client connected to #{@host}:#{@port} against DB #{@db}"
102
+ end
103
+
104
+ def connect_to_server
105
+ @sock = connect_to(@host,@port,@timeout == 0 ? nil : @timeout)
106
+ call_command(["auth",@pass]) if !@pass.empty?
107
+ call_command(["select",@db]) if @db != 0
108
+ end
109
+
110
+ def connect_to(host, port, timeout=nil)
111
+ # We support connect() timeout only if system_timer is availabe
112
+ # or if we are running against Ruby >= 1.9
113
+ # Timeout reading from the socket instead will be supported anyway.
114
+ if @timeout != 0 and RedisTimer
115
+ begin
116
+ sock = TCPSocket.new(host, port, 0)
117
+ rescue Timeout::Error
118
+ @sock = nil
119
+ raise Timeout::Error, "Timeout connecting to the server"
120
+ end
121
+ else
122
+ sock = TCPSocket.new(host, port, 0)
123
+ end
124
+ sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
125
+
126
+ # If the timeout is set we set the low level socket options in order
127
+ # to make sure a blocking read will return after the specified number
128
+ # of seconds. This hack is from memcached ruby client.
129
+ if timeout
130
+ secs = Integer(timeout)
131
+ usecs = Integer((timeout - secs) * 1_000_000)
132
+ optval = [secs, usecs].pack("l_2")
133
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
134
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
135
+ end
136
+ sock
137
+ end
138
+
139
+ def method_missing(*argv)
140
+ call_command(argv)
141
+ end
142
+
143
+ def call_command(argv)
144
+ # this wrapper to raw_call_command handle reconnection on socket
145
+ # error. We try to reconnect just one time, otherwise let the error
146
+ # araise.
147
+ connect_to_server if !@sock
148
+ begin
149
+ raw_call_command(argv)
150
+ rescue Errno::ECONNRESET
151
+ @sock.close
152
+ connect_to_server
153
+ raw_call_command(argv)
154
+ end
155
+ end
156
+
157
+ def raw_call_command(argv)
158
+ bulk = nil
159
+ argv[0] = argv[0].to_s.downcase
160
+ argv[0] = Aliases[argv[0]] if Aliases[argv[0]]
161
+ if BulkCommands[argv[0]] and argv.length > 1
162
+ bulk = argv[-1].to_s
163
+ argv[-1] = bulk.length
164
+ end
165
+ @sock.write(argv.join(" ")+"\r\n")
166
+ @sock.write(bulk+"\r\n") if bulk
167
+
168
+ # Post process the reply if needed
169
+ processor = ReplyProcessor[argv[0]]
170
+ processor ? processor.call(read_reply) : read_reply
171
+ end
172
+
173
+ def select(*args)
174
+ raise "SELECT not allowed, use the :db option when creating the object"
175
+ end
176
+
177
+ def [](key)
178
+ get(key)
179
+ end
180
+
181
+ def []=(key,value)
182
+ set(key,value)
183
+ end
184
+
185
+ def sort(key, opts={})
186
+ cmd = []
187
+ cmd << "SORT #{key}"
188
+ cmd << "BY #{opts[:by]}" if opts[:by]
189
+ cmd << "GET #{[opts[:get]].flatten * ' GET '}" if opts[:get]
190
+ cmd << "#{opts[:order]}" if opts[:order]
191
+ cmd << "LIMIT #{opts[:limit].join(' ')}" if opts[:limit]
192
+ call_command(cmd)
193
+ end
194
+
195
+ def incr(key,increment=nil)
196
+ call_command(increment ? ["incrby",key,increment] : ["incr",key])
197
+ end
198
+
199
+ def decr(key,decrement=nil)
200
+ call_command(decrement ? ["decrby",key,decrement] : ["decr",key])
201
+ end
202
+
203
+ def read_reply
204
+ # We read the first byte using read() mainly because gets() is
205
+ # immune to raw socket timeouts.
206
+ begin
207
+ rtype = @sock.read(1)
208
+ rescue Errno::EAGAIN
209
+ # We want to make sure it reconnects on the next command after the
210
+ # timeout. Otherwise the server may reply in the meantime leaving
211
+ # the protocol in a desync status.
212
+ @sock = nil
213
+ raise Errno::EAGAIN, "Timeout reading from the socket"
214
+ end
215
+
216
+ raise Errno::ECONNRESET,"Connection lost" if !rtype
217
+ line = @sock.gets
218
+ case rtype
219
+ when "-"
220
+ raise "-"+line.strip
221
+ when "+"
222
+ line.strip
223
+ when ":"
224
+ line.to_i
225
+ when "$"
226
+ bulklen = line.to_i
227
+ return nil if bulklen == -1
228
+ data = @sock.read(bulklen)
229
+ @sock.read(2) # CRLF
230
+ data
231
+ when "*"
232
+ objects = line.to_i
233
+ return nil if bulklen == -1
234
+ res = []
235
+ objects.times {
236
+ res << read_reply
237
+ }
238
+ res
239
+ else
240
+ raise "Protocol error, got '#{rtype}' as initial reply byte"
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,33 @@
1
+ Gem::Specification.new do |s|
2
+ s.specification_version = 2 if s.respond_to? :specification_version=
3
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
+
5
+ s.name = 'sinatra-redis'
6
+ s.version = '0.1.0'
7
+ s.date = '2009-09-21'
8
+
9
+ s.description = "Extends Sinatra with redis helpers for instant redis use"
10
+ s.summary = s.description
11
+
12
+ s.authors = ["Blake Mizerany"]
13
+ s.email = "blake.mizerany@gmail.com"
14
+
15
+ # = MANIFEST =
16
+ s.files = %w[
17
+ README.md
18
+ lib/sinatra/redis.rb
19
+ lib/sinatra/redis/rubyredis.rb
20
+ sinatra-redis.gemspec
21
+ ]
22
+ # = MANIFEST =
23
+
24
+ s.extra_rdoc_files = %w[README.md]
25
+ s.add_dependency 'sinatra', '>= 0.9.4'
26
+
27
+ s.has_rdoc = true
28
+ s.homepage = "http://github.com/rtomayko/sinatra-redis"
29
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Sinatra::Redis"]
30
+ s.require_paths = %w[lib]
31
+ s.rubyforge_project = 'bmizerany'
32
+ s.rubygems_version = '1.1.1'
33
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-redis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Blake Mizerany
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-21 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sinatra
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.4
24
+ version:
25
+ description: Extends Sinatra with redis helpers for instant redis use
26
+ email: blake.mizerany@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.md
33
+ files:
34
+ - README.md
35
+ - lib/sinatra/redis.rb
36
+ - lib/sinatra/redis/rubyredis.rb
37
+ - sinatra-redis.gemspec
38
+ has_rdoc: true
39
+ homepage: http://github.com/rtomayko/sinatra-redis
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --line-numbers
45
+ - --inline-source
46
+ - --title
47
+ - Sinatra::Redis
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project: bmizerany
65
+ rubygems_version: 1.3.4
66
+ signing_key:
67
+ specification_version: 2
68
+ summary: Extends Sinatra with redis helpers for instant redis use
69
+ test_files: []
70
+