sinatra-redis 0.1.0

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/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
+