firehose 1.1.1 → 1.2.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.
- checksums.yaml +15 -0
- data/.rbenv-version +1 -1
- data/README.md +1 -1
- data/config/rainbows.rb +4 -3
- data/lib/firehose.rb +6 -8
- data/lib/firehose/assets.rb +1 -3
- data/lib/firehose/cli.rb +6 -8
- data/lib/firehose/client.rb +2 -84
- data/lib/firehose/client/consumer.rb +94 -0
- data/lib/firehose/client/producer.rb +106 -0
- data/lib/firehose/logging.rb +1 -4
- data/lib/{rainbows_em_swf_policy.rb → firehose/patches/rainbows.rb} +4 -2
- data/lib/firehose/patches/swf_policy_request.rb +26 -0
- data/lib/{thin_em_swf_policy.rb → firehose/patches/thin.rb} +2 -1
- data/lib/firehose/rack.rb +12 -39
- data/lib/firehose/rack/app.rb +42 -0
- data/lib/firehose/rack/{consumer_app.rb → consumer.rb} +7 -5
- data/lib/firehose/rack/{ping_app.rb → ping.rb} +4 -2
- data/lib/firehose/rack/{publisher_app.rb → publisher.rb} +3 -3
- data/lib/firehose/server.rb +16 -42
- data/lib/firehose/server/app.rb +53 -0
- data/lib/firehose/server/channel.rb +80 -0
- data/lib/firehose/server/publisher.rb +134 -0
- data/lib/firehose/server/subscriber.rb +50 -0
- data/lib/firehose/version.rb +2 -2
- data/spec/integrations/integration_test_helper.rb +2 -2
- data/spec/integrations/shared_examples.rb +3 -3
- data/spec/lib/{client_spec.rb → client/consumer_spec.rb} +0 -0
- data/spec/lib/{producer_spec.rb → client/producer_spec.rb} +13 -13
- data/spec/lib/firehose_spec.rb +7 -0
- data/spec/lib/rack/{consumer_app_spec.rb → consumer_spec.rb} +2 -2
- data/spec/lib/rack/{ping_app_spec.rb → ping_spec.rb} +3 -3
- data/spec/lib/rack/{publisher_app_spec.rb → publisher_spec.rb} +3 -3
- data/spec/lib/server/app_spec.rb +1 -0
- data/spec/lib/{channel_spec.rb → server/channel_spec.rb} +4 -4
- data/spec/lib/{publisher_spec.rb → server/publisher_spec.rb} +9 -9
- data/spec/lib/{subscriber_spec.rb → server/subscriber_spec.rb} +4 -4
- data/spec/spec_helper.rb +0 -5
- metadata +38 -77
- data/lib/firehose/channel.rb +0 -84
- data/lib/firehose/default.rb +0 -8
- data/lib/firehose/producer.rb +0 -104
- data/lib/firehose/publisher.rb +0 -127
- data/lib/firehose/subscriber.rb +0 -54
- data/lib/firehose/swf_policy_request.rb +0 -23
- data/spec/lib/broker_spec.rb +0 -30
- data/spec/lib/consumer_spec.rb +0 -66
- data/spec/lib/default_spec.rb +0 -7
data/lib/firehose/channel.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
module Firehose
|
2
|
-
class Channel
|
3
|
-
attr_reader :channel_key, :redis, :subscriber, :list_key, :sequence_key
|
4
|
-
|
5
|
-
def self.redis
|
6
|
-
@redis ||= EM::Hiredis.connect
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.subscriber
|
10
|
-
@subscriber ||= Subscriber.new(EM::Hiredis.connect)
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
def initialize(channel_key, redis=self.class.redis, subscriber=self.class.subscriber)
|
15
|
-
@channel_key, @redis, @subscriber = channel_key, redis, subscriber
|
16
|
-
|
17
|
-
@list_key, @sequence_key = key(channel_key, :list), key(channel_key, :sequence)
|
18
|
-
end
|
19
|
-
|
20
|
-
def next_message(last_sequence=nil, options={})
|
21
|
-
last_sequence = last_sequence.to_i
|
22
|
-
|
23
|
-
deferrable = EM::DefaultDeferrable.new
|
24
|
-
# TODO - Think this through a little harder... maybe some tests ol buddy!
|
25
|
-
deferrable.errback {|e| EM.next_tick { raise e } unless [:timeout, :disconnect].include?(e) }
|
26
|
-
|
27
|
-
|
28
|
-
# TODO: Use HSET so we don't have to pull 100 messages back every time.
|
29
|
-
redis.multi
|
30
|
-
redis.get(sequence_key).
|
31
|
-
errback {|e| deferrable.fail e }
|
32
|
-
redis.lrange(list_key, 0, Firehose::Publisher::MAX_MESSAGES).
|
33
|
-
errback {|e| deferrable.fail e }
|
34
|
-
redis.exec.callback do |(sequence, message_list)|
|
35
|
-
Firehose.logger.debug "exec returened: `#{sequence}` and `#{message_list.inspect}`"
|
36
|
-
sequence = sequence.to_i
|
37
|
-
|
38
|
-
if sequence.nil? || (diff = sequence - last_sequence) <= 0
|
39
|
-
Firehose.logger.debug "No message available yet, subscribing. sequence: `#{sequence}`"
|
40
|
-
# Either this resource has never been seen before or we are all caught up.
|
41
|
-
# Subscribe and hope something gets published to this end-point.
|
42
|
-
subscribe(deferrable, options[:timeout])
|
43
|
-
elsif last_sequence > 0 && diff < Firehose::Publisher::MAX_MESSAGES
|
44
|
-
# The client is kinda-sorta running behind, but has a chance to catch
|
45
|
-
# up. Catch them up FTW.
|
46
|
-
# But we won't "catch them up" if last_sequence was zero/nil because
|
47
|
-
# that implies the client is connecting for the 1st time.
|
48
|
-
message = message_list[diff-1]
|
49
|
-
Firehose.logger.debug "Sending old message `#{message}` and sequence `#{sequence}` to client directly. Client is `#{diff}` behind, at `#{last_sequence}`."
|
50
|
-
deferrable.succeed message, last_sequence + 1
|
51
|
-
else
|
52
|
-
# The client is hopelessly behind and underwater. Just reset
|
53
|
-
# their whole world with the lastest message.
|
54
|
-
message = message_list[0]
|
55
|
-
Firehose.logger.debug "Sending latest message `#{message}` and sequence `#{sequence}` to client directly."
|
56
|
-
deferrable.succeed message, sequence
|
57
|
-
end
|
58
|
-
end.errback {|e| deferrable.fail e }
|
59
|
-
|
60
|
-
deferrable
|
61
|
-
end
|
62
|
-
|
63
|
-
def unsubscribe(deferrable)
|
64
|
-
subscriber.unsubscribe channel_key, deferrable
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
def key(*segments)
|
69
|
-
segments.unshift(:firehose).join(':')
|
70
|
-
end
|
71
|
-
|
72
|
-
def subscribe(deferrable, timeout=nil)
|
73
|
-
subscriber.subscribe(channel_key, deferrable)
|
74
|
-
if timeout
|
75
|
-
timer = EventMachine::Timer.new(timeout) do
|
76
|
-
deferrable.fail :timeout
|
77
|
-
unsubscribe deferrable
|
78
|
-
end
|
79
|
-
# Cancel the timer if when the deferrable succeeds
|
80
|
-
deferrable.callback { timer.cancel }
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
data/lib/firehose/default.rb
DELETED
data/lib/firehose/producer.rb
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
# TODO Move this into the Firehose::Client:Producer namespace and rename the class to Http (its an HTTP publisher dumby!)
|
2
|
-
require "faraday"
|
3
|
-
require "uri"
|
4
|
-
|
5
|
-
module Firehose
|
6
|
-
# Publish messages to Firehose via an HTTP interface.
|
7
|
-
class Producer
|
8
|
-
|
9
|
-
# Exception gets raised when a 202 is _not_ received from the server after a message is published.
|
10
|
-
PublishError = Class.new(RuntimeError)
|
11
|
-
TimeoutError = Class.new(Faraday::Error::TimeoutError)
|
12
|
-
Timeout = 1 # How many seconds should we wait for a publish to take?
|
13
|
-
|
14
|
-
# A DSL for publishing requests. This doesn't so much, but lets us call
|
15
|
-
# Firehose::Producer#publish('message').to('channel'). Slick eh? If you don't like it,
|
16
|
-
# just all Firehose::Producer#put('message', 'channel')
|
17
|
-
class Builder
|
18
|
-
def initialize(producer, message)
|
19
|
-
@producer, @message = producer, message
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
def to(channel, opts={}, &callback)
|
24
|
-
@producer.put(@message, channel, opts, &callback)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# URI for the Firehose server. This URI does not include the path of the channel.
|
29
|
-
attr_reader :uri
|
30
|
-
|
31
|
-
def initialize(uri = Firehose::Default::URI)
|
32
|
-
@uri = URI.parse(uri.to_s)
|
33
|
-
@uri.scheme ||= 'http'
|
34
|
-
end
|
35
|
-
|
36
|
-
# A DSL for publishing messages.
|
37
|
-
def publish(message)
|
38
|
-
Builder.new(self, message)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Publish the message via HTTP.
|
42
|
-
def put(message, channel, opts, &block)
|
43
|
-
ttl = opts[:ttl]
|
44
|
-
|
45
|
-
response = conn.put do |req|
|
46
|
-
req.options[:timeout] = Timeout
|
47
|
-
if conn.path_prefix.nil? || conn.path_prefix == '/'
|
48
|
-
# This avoids a double / if the channel starts with a / too (which is expected).
|
49
|
-
req.path = channel
|
50
|
-
else
|
51
|
-
if conn.path_prefix =~ /\/\Z/ || channel =~ /\A\//
|
52
|
-
req.path = [conn.path_prefix, channel].compact.join
|
53
|
-
else
|
54
|
-
# Add a / so the prefix and channel aren't just rammed together.
|
55
|
-
req.path = [conn.path_prefix, channel].compact.join('/')
|
56
|
-
end
|
57
|
-
end
|
58
|
-
req.body = message
|
59
|
-
req.headers['Cache-Control'] = "max-age=#{ttl.to_i}" if ttl
|
60
|
-
end
|
61
|
-
response.on_complete do
|
62
|
-
case response.status
|
63
|
-
when 202 # Fire off the callback if everything worked out OK.
|
64
|
-
block.call(response) if block
|
65
|
-
else
|
66
|
-
error_handler.call PublishError.new("Could not publish #{message.inspect} to '#{uri.to_s}/#{channel}': #{response.inspect}")
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# Hide Faraday with this Timeout exception, and through the error handler.
|
71
|
-
rescue Faraday::Error::TimeoutError => e
|
72
|
-
error_handler.call TimeoutError.new(e)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Handle errors that could happen while publishing a message.
|
76
|
-
def on_error(&block)
|
77
|
-
@error_handler = block
|
78
|
-
end
|
79
|
-
|
80
|
-
# Raise an exception if an error occurs when connecting to the Firehose.
|
81
|
-
def error_handler
|
82
|
-
@error_handler || Proc.new{ |e| raise e }
|
83
|
-
end
|
84
|
-
|
85
|
-
# What adapter should Firehose use to PUT the message? List of adapters is
|
86
|
-
# available at https://github.com/technoweenie/faraday.
|
87
|
-
def self.adapter=(adapter)
|
88
|
-
@adapter = adapter
|
89
|
-
end
|
90
|
-
|
91
|
-
# Use :net_http for the default Faraday adapter.
|
92
|
-
def self.adapter
|
93
|
-
@adapter ||= Faraday.default_adapter
|
94
|
-
end
|
95
|
-
|
96
|
-
private
|
97
|
-
# Build out a Faraday connection
|
98
|
-
def conn
|
99
|
-
@conn ||= Faraday.new(:url => uri.to_s) do |builder|
|
100
|
-
builder.adapter self.class.adapter
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
data/lib/firehose/publisher.rb
DELETED
@@ -1,127 +0,0 @@
|
|
1
|
-
module Firehose
|
2
|
-
class Publisher
|
3
|
-
|
4
|
-
MAX_MESSAGES = 100
|
5
|
-
TTL = 60*60*24 # 1 day of time, yay!
|
6
|
-
PAYLOAD_DELIMITER = "\n"
|
7
|
-
|
8
|
-
def publish(channel_key, message, opts={})
|
9
|
-
# How long should we hang on to the resource once is published?
|
10
|
-
ttl = (opts[:ttl] || TTL).to_i
|
11
|
-
|
12
|
-
# TODO hi-redis isn't that awesome... we have to setup an errback per even for wrong
|
13
|
-
# commands because of the lack of a method_missing whitelist. Perhaps implement a whitelist in
|
14
|
-
# em-hiredis or us a diff lib?
|
15
|
-
if (deferrable = opts[:deferrable]).nil?
|
16
|
-
deferrable = EM::DefaultDeferrable.new
|
17
|
-
deferrable.errback do |e|
|
18
|
-
# Handle missing Lua publishing script in cache
|
19
|
-
# (such as Redis restarting or someone executing SCRIPT FLUSH)
|
20
|
-
if e.message =~ /NOSCRIPT/
|
21
|
-
deferrable.succeed
|
22
|
-
EM.next_tick do
|
23
|
-
@publish_script_digest = nil
|
24
|
-
combined_opts = opts.merge :deferrable => deferrable
|
25
|
-
self.publish channel_key, message, combined_opts
|
26
|
-
end
|
27
|
-
else
|
28
|
-
EM.next_tick { raise e }
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
if @publish_script_digest.nil?
|
34
|
-
register_publish_script.errback do |e|
|
35
|
-
deferrable.fail e
|
36
|
-
end.callback do |digest|
|
37
|
-
@publish_script_digest = digest
|
38
|
-
Firehose.logger.debug "Registered Lua publishing script with Redis => #{digest}"
|
39
|
-
eval_publish_script channel_key, message, ttl, deferrable
|
40
|
-
end
|
41
|
-
else
|
42
|
-
eval_publish_script channel_key, message, ttl, deferrable
|
43
|
-
end
|
44
|
-
|
45
|
-
deferrable
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
def key(*segments)
|
50
|
-
segments.unshift(:firehose).join(':')
|
51
|
-
end
|
52
|
-
|
53
|
-
def redis
|
54
|
-
@redis ||= EM::Hiredis.connect
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.to_payload(channel_key, sequence, message)
|
58
|
-
[channel_key, sequence, message].join(PAYLOAD_DELIMITER)
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.from_payload(payload)
|
62
|
-
payload.split(PAYLOAD_DELIMITER, method(:to_payload).arity)
|
63
|
-
end
|
64
|
-
|
65
|
-
# TODO: Make this FAR more robust. Ideally we'd whitelist the permitted
|
66
|
-
# characters and then escape or remove everything else.
|
67
|
-
# See: http://en.wikibooks.org/wiki/Lua_Programming/How_to_Lua/escape_sequence
|
68
|
-
def lua_escape(str)
|
69
|
-
str.gsub(/\\/,'\\\\\\').gsub(/"/,'\"').gsub(/\n/,'\n').gsub(/\r/,'\r')
|
70
|
-
end
|
71
|
-
|
72
|
-
def register_publish_script
|
73
|
-
redis.script 'LOAD', REDIS_PUBLISH_SCRIPT
|
74
|
-
end
|
75
|
-
|
76
|
-
def eval_publish_script(channel_key, message, ttl, deferrable)
|
77
|
-
list_key = key(channel_key, :list)
|
78
|
-
script_args = [
|
79
|
-
key(channel_key, :sequence),
|
80
|
-
list_key,
|
81
|
-
key(:channel_updates),
|
82
|
-
ttl,
|
83
|
-
message,
|
84
|
-
MAX_MESSAGES,
|
85
|
-
PAYLOAD_DELIMITER,
|
86
|
-
channel_key
|
87
|
-
]
|
88
|
-
redis.evalsha(
|
89
|
-
@publish_script_digest, script_args.length, *script_args
|
90
|
-
).errback do |e|
|
91
|
-
deferrable.fail e
|
92
|
-
end.callback do |sequence|
|
93
|
-
Firehose.logger.debug "Redis stored/published `#{message}` to list `#{list_key}` with sequence `#{sequence}`"
|
94
|
-
deferrable.succeed
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
REDIS_PUBLISH_SCRIPT = <<-LUA
|
99
|
-
local sequence_key = KEYS[1]
|
100
|
-
local list_key = KEYS[2]
|
101
|
-
local channel_key = KEYS[3]
|
102
|
-
local ttl = KEYS[4]
|
103
|
-
local message = KEYS[5]
|
104
|
-
local max_messages = KEYS[6]
|
105
|
-
local payload_delimiter = KEYS[7]
|
106
|
-
local firehose_resource = KEYS[8]
|
107
|
-
|
108
|
-
local current_sequence = redis.call('get', sequence_key)
|
109
|
-
if current_sequence == nil or current_sequence == false then
|
110
|
-
current_sequence = 0
|
111
|
-
end
|
112
|
-
|
113
|
-
local sequence = current_sequence + 1
|
114
|
-
local message_payload = firehose_resource .. payload_delimiter .. sequence .. payload_delimiter .. message
|
115
|
-
|
116
|
-
redis.call('set', sequence_key, sequence)
|
117
|
-
redis.call('expire', sequence_key, ttl)
|
118
|
-
redis.call('lpush', list_key, message)
|
119
|
-
redis.call('ltrim', list_key, 0, max_messages - 1)
|
120
|
-
redis.call('expire', list_key, ttl)
|
121
|
-
redis.call('publish', channel_key, message_payload)
|
122
|
-
|
123
|
-
return sequence
|
124
|
-
LUA
|
125
|
-
|
126
|
-
end
|
127
|
-
end
|
data/lib/firehose/subscriber.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
module Firehose
|
2
|
-
# Setups a connetion to Redis to listen for new resources...
|
3
|
-
class Subscriber
|
4
|
-
attr_reader :pubsub
|
5
|
-
|
6
|
-
def initialize(redis)
|
7
|
-
@pubsub = redis.pubsub
|
8
|
-
|
9
|
-
# TODO: Instead of just raising an exception, it would probably be better
|
10
|
-
# for the errback to set some sort of 'disconnected' state. Then
|
11
|
-
# whenever a deferrable was 'subscribed' we could instantly fail
|
12
|
-
# the deferrable with whatever connection error we had.
|
13
|
-
# An alternative which would have a similar result would be to
|
14
|
-
# subscribe lazily (i.e. not until we have a deferrable to subscribe).
|
15
|
-
# Then, if connecting failed, it'd be super easy to fail the deferrable
|
16
|
-
# with the same error.
|
17
|
-
# The final goal is to allow the failed deferrable bubble back up
|
18
|
-
# so we can send back a nice, clean 500 error to the client.
|
19
|
-
pubsub.subscribe('firehose:channel_updates').
|
20
|
-
errback{|e| EM.next_tick { raise e } }.
|
21
|
-
callback { Firehose.logger.debug "Redis subscribed to `firehose:channel_updates`" }
|
22
|
-
pubsub.on(:message) do |_, payload|
|
23
|
-
channel_key, sequence, message = Firehose::Publisher.from_payload(payload)
|
24
|
-
|
25
|
-
if deferrables = subscriptions.delete(channel_key)
|
26
|
-
Firehose.logger.debug "Redis notifying #{deferrables.count} deferrable(s) at `#{channel_key}` with sequence `#{sequence}` and message `#{message}`"
|
27
|
-
deferrables.each do |deferrable|
|
28
|
-
Firehose.logger.debug "Sending message #{message} and sequence #{sequence} to client from subscriber"
|
29
|
-
deferrable.succeed message, sequence.to_i
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def subscribe(channel_key, deferrable)
|
36
|
-
subscriptions[channel_key].push deferrable
|
37
|
-
end
|
38
|
-
|
39
|
-
def unsubscribe(channel_key, deferrable)
|
40
|
-
subscriptions[channel_key].delete deferrable
|
41
|
-
subscriptions.delete(channel_key) if subscriptions[channel_key].empty?
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
private
|
46
|
-
def subscriptions
|
47
|
-
@subscriptions ||= Hash.new{|h,k| h[k] = []}
|
48
|
-
end
|
49
|
-
|
50
|
-
def key(*segments)
|
51
|
-
segments.unshift(:firehose).join(':')
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module Firehose
|
2
|
-
module SwfPolicyRequest
|
3
|
-
|
4
|
-
# Borrowed from: https://github.com/igrigorik/em-websocket/blob/3e7f7d7760cc23b9d1d34fc1c17bab4423b5d11a/lib/em-websocket/connection.rb#L104
|
5
|
-
def handle_swf_policy_request(data)
|
6
|
-
if data =~ /\A<policy-file-request\s*\/>/
|
7
|
-
Firehose.logger.debug "Received SWF Policy request: #{data.inspect}"
|
8
|
-
send_data policy
|
9
|
-
close_connection_after_writing
|
10
|
-
true
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def policy
|
15
|
-
<<-EOS
|
16
|
-
<?xml version="1.0"?>
|
17
|
-
<cross-domain-policy>
|
18
|
-
<allow-access-from domain="*" to-ports="*"/>
|
19
|
-
</cross-domain-policy>
|
20
|
-
EOS
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/spec/lib/broker_spec.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# require 'spec_helper'
|
2
|
-
|
3
|
-
# describe Firehose::Broker do
|
4
|
-
# include EM::TestHelper
|
5
|
-
|
6
|
-
# let(:broker) { Firehose::Broker.new }
|
7
|
-
|
8
|
-
# it "should unsubscibe consumers and remove them from the collection" do
|
9
|
-
# stats = nil
|
10
|
-
|
11
|
-
# em do
|
12
|
-
# broker.consumer('1').subscribe_to('/the-channel')
|
13
|
-
# broker.consumer('2').subscribe_to('/the-channel')
|
14
|
-
# broker.consumer('2').subscribe_to('/a-channel')
|
15
|
-
|
16
|
-
# em.add_timer(1) do
|
17
|
-
# stats = broker.stats
|
18
|
-
# broker.stop
|
19
|
-
# em.stop
|
20
|
-
# end
|
21
|
-
# end
|
22
|
-
|
23
|
-
# stats.should == {
|
24
|
-
# '1' => {'subscriptions' => ['/the-channel'] },
|
25
|
-
# '2' => {'subscriptions' => ['/the-channel', '/a-channel']}
|
26
|
-
# }
|
27
|
-
|
28
|
-
# broker.stats.should == {}
|
29
|
-
# end
|
30
|
-
# end
|
data/spec/lib/consumer_spec.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
# require 'spec_helper'
|
2
|
-
|
3
|
-
# describe Firehose::Consumer do
|
4
|
-
# include EM::TestHelper
|
5
|
-
|
6
|
-
# let(:consumer) { Firehose::Consumer.new }
|
7
|
-
# let(:publisher) { Firehose::Publisher.new }
|
8
|
-
# let(:channel) { '/papa-smurf' }
|
9
|
-
# let(:another_channel) { '/mama-smurf' }
|
10
|
-
|
11
|
-
# describe "subscriptions" do
|
12
|
-
# it "should subscribe to channel" do
|
13
|
-
# sent, recieved = 'hi', nil
|
14
|
-
|
15
|
-
# em do
|
16
|
-
# consumer.subscribe_to channel do |msg|
|
17
|
-
# recieved = msg
|
18
|
-
# em.stop
|
19
|
-
# end
|
20
|
-
# em.add_timer(1) do
|
21
|
-
# publisher.publish(channel, sent)
|
22
|
-
# end
|
23
|
-
# end
|
24
|
-
|
25
|
-
# recieved.should == sent
|
26
|
-
# end
|
27
|
-
|
28
|
-
# it "should track subscriptions" do
|
29
|
-
# lambda{
|
30
|
-
# em do
|
31
|
-
# consumer.subscribe_to channel
|
32
|
-
# consumer.subscribe_to another_channel
|
33
|
-
# em.add_timer(1){ em.stop }
|
34
|
-
# end
|
35
|
-
# }.should change{ consumer.subscriptions.size }.by(2)
|
36
|
-
# end
|
37
|
-
|
38
|
-
# it "should only allow one subscription per channel" do
|
39
|
-
# lambda{
|
40
|
-
# em do
|
41
|
-
# 3.times { consumer.subscribe_to channel }
|
42
|
-
# em.add_timer(1){ em.stop }
|
43
|
-
# end
|
44
|
-
# }.should change{ consumer.subscriptions.size }.by(1)
|
45
|
-
# end
|
46
|
-
|
47
|
-
# it "should unsubscribe from all channels" do
|
48
|
-
# subscribed_count, after_unsubscribe_count = 0, nil
|
49
|
-
|
50
|
-
# em do
|
51
|
-
# consumer.subscribe_to channel
|
52
|
-
# consumer.subscribe_to another_channel
|
53
|
-
# subscribed_count = consumer.subscriptions.size
|
54
|
-
# em.add_timer(1) do
|
55
|
-
# consumer.unsubscribe
|
56
|
-
# em.add_timer(1) do
|
57
|
-
# em.stop
|
58
|
-
# end
|
59
|
-
# end
|
60
|
-
# end
|
61
|
-
|
62
|
-
# subscribed_count.should == 2
|
63
|
-
# consumer.subscriptions.size.should == 0
|
64
|
-
# end
|
65
|
-
# end
|
66
|
-
# end
|