em-hiredis 0.0.1

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