em-hiredis 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README.md +61 -0
- data/Rakefile +2 -0
- data/em-hiredis.gemspec +23 -0
- data/lib/em-hiredis/client.rb +131 -0
- data/lib/em-hiredis/connection.rb +61 -0
- data/lib/em-hiredis/event_emitter.rb +29 -0
- data/lib/em-hiredis/version.rb +5 -0
- data/lib/em-hiredis.rb +22 -0
- metadata +76 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
Getting started
|
2
|
+
===============
|
3
|
+
|
4
|
+
Connect to redis
|
5
|
+
|
6
|
+
redis_client = EM::Hiredis.connect
|
7
|
+
|
8
|
+
The client is a deferrable which succeeds when the underlying connection is established so you can bind to this. This isn't necessary however - any commands sent before the connection is established (or while reconnecting) will be sent to redis on connect.
|
9
|
+
|
10
|
+
redis_client.callback { puts "Redis now connected" }
|
11
|
+
|
12
|
+
All redis commands are available without any remapping of names
|
13
|
+
|
14
|
+
redis.set('foo', 'bar').callback {
|
15
|
+
redis.get('foo').callback { |value|
|
16
|
+
p [:returned, value]
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
As a shortcut, if you're only interested in binding to the success case you can simply provide a block to any command
|
21
|
+
|
22
|
+
redis.get('foo') { |value|
|
23
|
+
p [:returned, value]
|
24
|
+
}
|
25
|
+
|
26
|
+
Handling failure
|
27
|
+
----------------
|
28
|
+
|
29
|
+
All commands return a deferrable. In the case that redis replies with an error (for example you called a hash operation against a set), or in the case that the redis connection is broken before the command returns, the deferrable will fail. If you care about the failure case you should bind to the errback - for example:
|
30
|
+
|
31
|
+
redis.sadd('aset', 'member').callback {
|
32
|
+
response_deferrable = redis.hget('aset', 'member')
|
33
|
+
response_deferrable.errback { |e|
|
34
|
+
p e # => #<RuntimeError: ERR Operation against a key holding the wrong kind of value>
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
Pubsub
|
39
|
+
------
|
40
|
+
|
41
|
+
This example should explain things. Once a redis connection is in a pubsub state, you must make sure you only send pubsub commands.
|
42
|
+
|
43
|
+
redis = EM::Hiredis::Client.connect
|
44
|
+
subscriber = EM::Hiredis::Client.connect
|
45
|
+
|
46
|
+
subscriber.subscribe('bar.0')
|
47
|
+
subscriber.psubscribe('bar.*')
|
48
|
+
|
49
|
+
subscriber.on(:message) { |channel, message|
|
50
|
+
p [:message, channel, message]
|
51
|
+
}
|
52
|
+
|
53
|
+
subscriber.on(:pmessage) { |key, channel, message|
|
54
|
+
p [:pmessage, key, channel, message]
|
55
|
+
}
|
56
|
+
|
57
|
+
EM.add_periodic_timer(1) {
|
58
|
+
redis.publish("bar.#{rand(2)}", "hello").errback { |e|
|
59
|
+
p [:publisherror, e]
|
60
|
+
}
|
61
|
+
}
|
data/Rakefile
ADDED
data/em-hiredis.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "em-hiredis/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "em-hiredis"
|
7
|
+
s.version = EM::Hiredis::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Martyn Loughran"]
|
10
|
+
s.email = ["me@mloughran.com"]
|
11
|
+
s.homepage = "http://github.com/mloughran/em-hiredis"
|
12
|
+
s.summary = %q{Eventmachine redis client}
|
13
|
+
s.description = %q{Eventmachine redis client using hiredis native parser}
|
14
|
+
|
15
|
+
s.add_dependency 'hiredis', '~> 0.2.0'
|
16
|
+
|
17
|
+
s.rubyforge_project = "em-hiredis"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module EM::Hiredis
|
2
|
+
class Client
|
3
|
+
PUBSUB_MESSAGES = %w{message pmessage}.freeze
|
4
|
+
|
5
|
+
include EM::Hiredis::EventEmitter
|
6
|
+
include EM::Deferrable
|
7
|
+
|
8
|
+
def self.connect(host = 'localhost', port = 6379)
|
9
|
+
new(host, port)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(host, port)
|
13
|
+
@host, @port = host, port
|
14
|
+
@subs, @psubs = [], []
|
15
|
+
@defs = []
|
16
|
+
@connection = EM.connect(host, port, Connection, host, port)
|
17
|
+
|
18
|
+
@connection.on(:closed) {
|
19
|
+
if @connected
|
20
|
+
@defs.each { |d| d.fail("Redis disconnected") }
|
21
|
+
@defs = []
|
22
|
+
@deferred_status = nil
|
23
|
+
@connected = false
|
24
|
+
@reconnecting = true
|
25
|
+
reconnect
|
26
|
+
else
|
27
|
+
EM.add_timer(1) { reconnect }
|
28
|
+
end
|
29
|
+
}
|
30
|
+
|
31
|
+
@connection.on(:connected) {
|
32
|
+
@connected = true
|
33
|
+
select(@db) if @db
|
34
|
+
@subs.each { |s| method_missing(:subscribe, s) }
|
35
|
+
@psubs.each { |s| method_missing(:psubscribe, s) }
|
36
|
+
succeed
|
37
|
+
|
38
|
+
if @reconnecting
|
39
|
+
@reconnecting = false
|
40
|
+
emit(:reconnected)
|
41
|
+
end
|
42
|
+
}
|
43
|
+
|
44
|
+
@connection.on(:message) { |reply|
|
45
|
+
if RuntimeError === reply
|
46
|
+
raise "Replies out of sync: #{reply.inspect}" if @defs.empty?
|
47
|
+
deferred = @defs.shift
|
48
|
+
deferred.fail(reply) if deferred
|
49
|
+
else
|
50
|
+
if reply && PUBSUB_MESSAGES.include?(reply[0]) # reply can be nil
|
51
|
+
kind, subscription, d1, d2 = *reply
|
52
|
+
|
53
|
+
case kind.to_sym
|
54
|
+
when :message
|
55
|
+
emit(:message, subscription, d1)
|
56
|
+
when :pmessage
|
57
|
+
emit(:pmessage, subscription, d1, d2)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
raise "Replies out of sync: #{reply.inspect}" if @defs.empty?
|
61
|
+
deferred = @defs.shift
|
62
|
+
deferred.succeed(reply) if deferred
|
63
|
+
end
|
64
|
+
end
|
65
|
+
}
|
66
|
+
|
67
|
+
@connected = false
|
68
|
+
@reconnecting = false
|
69
|
+
end
|
70
|
+
|
71
|
+
# Indicates that commands have been sent to redis but a reply has not yet
|
72
|
+
# been received.
|
73
|
+
#
|
74
|
+
# This can be useful for example to avoid stopping the
|
75
|
+
# eventmachine reactor while there are outstanding commands
|
76
|
+
#
|
77
|
+
def pending_commands?
|
78
|
+
@connected && @defs.size > 0
|
79
|
+
end
|
80
|
+
|
81
|
+
def subscribe(channel)
|
82
|
+
@subs << channel
|
83
|
+
method_missing(:subscribe, channel)
|
84
|
+
end
|
85
|
+
|
86
|
+
def unsubscribe(channel)
|
87
|
+
@subs.delete(channel)
|
88
|
+
method_missing(:unsubscribe, channel)
|
89
|
+
end
|
90
|
+
|
91
|
+
def psubscribe(channel)
|
92
|
+
@psubs << channel
|
93
|
+
method_missing(:psubscribe, channel)
|
94
|
+
end
|
95
|
+
|
96
|
+
def punsubscribe(channel)
|
97
|
+
@psubs.delete(channel)
|
98
|
+
method_missing(:punsubscribe, channel)
|
99
|
+
end
|
100
|
+
|
101
|
+
def select(db)
|
102
|
+
@db = db
|
103
|
+
method_missing(:select, db)
|
104
|
+
end
|
105
|
+
|
106
|
+
def method_missing(sym, *args)
|
107
|
+
deferred = EM::DefaultDeferrable.new
|
108
|
+
# Shortcut for defining the callback case with just a block
|
109
|
+
deferred.callback { |result| yield(result) } if block_given?
|
110
|
+
|
111
|
+
if @connected
|
112
|
+
@connection.send_command(sym, *args)
|
113
|
+
@defs.push(deferred)
|
114
|
+
else
|
115
|
+
callback {
|
116
|
+
@connection.send_command(sym, *args)
|
117
|
+
@defs.push(deferred)
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
return deferred
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def reconnect
|
127
|
+
EM::Hiredis.logger.debug("Trying to reconnect to Redis")
|
128
|
+
@connection.reconnect @host, @port
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'hiredis/reader'
|
2
|
+
|
3
|
+
module EM::Hiredis
|
4
|
+
class Connection < EM::Connection
|
5
|
+
include EM::Hiredis::EventEmitter
|
6
|
+
|
7
|
+
def initialize(host, port)
|
8
|
+
super
|
9
|
+
@host, @port = host, port
|
10
|
+
end
|
11
|
+
|
12
|
+
def connection_completed
|
13
|
+
EM::Hiredis.logger.info("Connected to Redis")
|
14
|
+
@reader = ::Hiredis::Reader.new
|
15
|
+
emit(:connected)
|
16
|
+
end
|
17
|
+
|
18
|
+
def receive_data(data)
|
19
|
+
@reader.feed(data)
|
20
|
+
until (reply = @reader.gets) == false
|
21
|
+
emit(:message, reply)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def unbind
|
26
|
+
EM::Hiredis.logger.info("Disconnected from Redis")
|
27
|
+
emit(:closed)
|
28
|
+
end
|
29
|
+
|
30
|
+
def send_command(sym, *args)
|
31
|
+
send_data(command(sym, *args))
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
COMMAND_DELIMITER = "\r\n"
|
37
|
+
|
38
|
+
def command(*args)
|
39
|
+
command = []
|
40
|
+
command << "*#{args.size}"
|
41
|
+
|
42
|
+
args.each do |arg|
|
43
|
+
arg = arg.to_s
|
44
|
+
command << "$#{string_size arg}"
|
45
|
+
command << arg
|
46
|
+
end
|
47
|
+
|
48
|
+
command.join(COMMAND_DELIMITER) + COMMAND_DELIMITER
|
49
|
+
end
|
50
|
+
|
51
|
+
if "".respond_to?(:bytesize)
|
52
|
+
def string_size(string)
|
53
|
+
string.to_s.bytesize
|
54
|
+
end
|
55
|
+
else
|
56
|
+
def string_size(string)
|
57
|
+
string.to_s.size
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module EM::Hiredis
|
2
|
+
module EventEmitter
|
3
|
+
def on(event, &listener)
|
4
|
+
_listeners[event] << listener
|
5
|
+
end
|
6
|
+
|
7
|
+
def emit(event, *args)
|
8
|
+
_listeners[event].each { |l| l.call(*args) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def remove_listener(event, &listener)
|
12
|
+
_listeners[event].delete(listener)
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_all_listeners(event)
|
16
|
+
_listeners.delete(event)
|
17
|
+
end
|
18
|
+
|
19
|
+
def listeners(event)
|
20
|
+
_listeners[event]
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def _listeners
|
26
|
+
@_listeners ||= Hash.new { |h,k| h[k] = [] }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/em-hiredis.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module EM
|
4
|
+
module Hiredis
|
5
|
+
class << self
|
6
|
+
attr_writer :logger
|
7
|
+
|
8
|
+
def logger
|
9
|
+
@logger ||= begin
|
10
|
+
require 'logger'
|
11
|
+
log = Logger.new(STDOUT)
|
12
|
+
log.level = Logger::WARN
|
13
|
+
log
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'em-hiredis/event_emitter'
|
21
|
+
require 'em-hiredis/connection'
|
22
|
+
require 'em-hiredis/client'
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: em-hiredis
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Martyn Loughran
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-03-11 00:00:00 -08:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: hiredis
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.2.0
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
description: Eventmachine redis client using hiredis native parser
|
28
|
+
email:
|
29
|
+
- me@mloughran.com
|
30
|
+
executables: []
|
31
|
+
|
32
|
+
extensions: []
|
33
|
+
|
34
|
+
extra_rdoc_files: []
|
35
|
+
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- Gemfile
|
39
|
+
- README.md
|
40
|
+
- Rakefile
|
41
|
+
- em-hiredis.gemspec
|
42
|
+
- lib/em-hiredis.rb
|
43
|
+
- lib/em-hiredis/client.rb
|
44
|
+
- lib/em-hiredis/connection.rb
|
45
|
+
- lib/em-hiredis/event_emitter.rb
|
46
|
+
- lib/em-hiredis/version.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: http://github.com/mloughran/em-hiredis
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project: em-hiredis
|
71
|
+
rubygems_version: 1.5.3
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Eventmachine redis client
|
75
|
+
test_files: []
|
76
|
+
|