em-hiredis 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in em-hiredis.gemspec
4
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -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
@@ -0,0 +1,5 @@
1
+ module EM
2
+ module Hiredis
3
+ VERSION = "0.0.1"
4
+ end
5
+ 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
+