firehose 1.3.6 → 2.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +31 -10
- data/{LICENSE.txt → LICENSE} +6 -6
- data/README.md +379 -187
- data/app/assets/javascripts/firehose.js +258 -0
- data/config/importmap.rb +1 -0
- data/db/migrate/20260214185244_create_firehose_events.rb +23 -0
- data/lib/commands/firehose_command.rb +119 -0
- data/lib/firehose/channel.rb +40 -0
- data/lib/firehose/cleanup_job.rb +21 -0
- data/lib/firehose/engine.rb +36 -0
- data/lib/firehose/helper.rb +40 -0
- data/lib/firehose/models/channel.rb +12 -0
- data/lib/firehose/models/message.rb +11 -0
- data/lib/firehose/queue.rb +44 -0
- data/lib/firehose/server.rb +930 -17
- data/lib/firehose/sse.rb +187 -0
- data/lib/firehose/stream.rb +32 -0
- data/lib/firehose/streamable.rb +25 -0
- data/lib/firehose/websocket.rb +242 -0
- data/lib/firehose.rb +67 -15
- data/lib/generators/firehose/install/install_generator.rb +43 -0
- data/lib/generators/firehose/install/templates/firehose_controller.rb +16 -0
- metadata +39 -366
- data/.codeclimate.yml +0 -29
- data/.dockerignore +0 -2
- data/.env.sample +0 -17
- data/.gitignore +0 -14
- data/.rspec +0 -1
- data/.rubocop.yml +0 -1156
- data/.ruby-version +0 -1
- data/.travis.yml +0 -12
- data/Dockerfile +0 -11
- data/Gemfile +0 -6
- data/Procfile +0 -1
- data/Procfile.dev +0 -3
- data/Rakefile +0 -64
- data/bin/firehose +0 -9
- data/coffeelint.json +0 -129
- data/config/rainbows.rb +0 -17
- data/docker-compose.yml +0 -17
- data/firehose.gemspec +0 -45
- data/karma.config.coffee +0 -89
- data/lib/assets/javascripts/firehose/base.js.coffee +0 -4
- data/lib/assets/javascripts/firehose/consumer.js.coffee +0 -86
- data/lib/assets/javascripts/firehose/core.js.coffee +0 -6
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +0 -210
- data/lib/assets/javascripts/firehose/multiplexed_consumer.js.coffee +0 -74
- data/lib/assets/javascripts/firehose/transport.js.coffee +0 -44
- data/lib/assets/javascripts/firehose/version.js.coffee.erb +0 -2
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +0 -148
- data/lib/assets/javascripts/firehose.js.coffee +0 -2
- data/lib/firehose/assets.rb +0 -56
- data/lib/firehose/cli.rb +0 -79
- data/lib/firehose/client/consumer.rb +0 -94
- data/lib/firehose/client/producer.rb +0 -114
- data/lib/firehose/client.rb +0 -10
- data/lib/firehose/logging.rb +0 -32
- data/lib/firehose/rack/app.rb +0 -49
- data/lib/firehose/rack/consumer/http_long_poll.rb +0 -159
- data/lib/firehose/rack/consumer/web_socket.rb +0 -200
- data/lib/firehose/rack/consumer.rb +0 -78
- data/lib/firehose/rack/ping.rb +0 -88
- data/lib/firehose/rack/publisher.rb +0 -59
- data/lib/firehose/rack.rb +0 -32
- data/lib/firehose/rails.rb +0 -7
- data/lib/firehose/server/app.rb +0 -52
- data/lib/firehose/server/channel.rb +0 -72
- data/lib/firehose/server/message_buffer.rb +0 -59
- data/lib/firehose/server/publisher.rb +0 -133
- data/lib/firehose/server/redis.rb +0 -32
- data/lib/firehose/server/subscriber.rb +0 -51
- data/lib/firehose/version.rb +0 -4
- data/package.json +0 -17
- data/spec/integrations/integration_test_helper.rb +0 -22
- data/spec/integrations/rainbows_spec.rb +0 -7
- data/spec/integrations/shared_examples.rb +0 -202
- data/spec/integrations/thin_spec.rb +0 -7
- data/spec/javascripts/firehose/base_spec.coffee +0 -2
- data/spec/javascripts/firehose/consumer_spec.coffee +0 -45
- data/spec/javascripts/firehose/long_poll_spec.coffee +0 -44
- data/spec/javascripts/firehose/multiplexed_consumer_spec.coffee +0 -72
- data/spec/javascripts/firehose/transport_spec.coffee +0 -30
- data/spec/javascripts/firehose/websocket_spec.coffee +0 -27
- data/spec/javascripts/helpers/spec_helper.js +0 -31
- data/spec/javascripts/support/jquery-1.11.1.js +0 -10308
- data/spec/javascripts/support/json2.js +0 -487
- data/spec/javascripts/support/sinon-1.7.3.js +0 -4290
- data/spec/javascripts/support/spec_helper.coffee +0 -3
- data/spec/lib/assets_spec.rb +0 -35
- data/spec/lib/client/consumer_spec.rb +0 -13
- data/spec/lib/client/producer_spec.rb +0 -71
- data/spec/lib/firehose_spec.rb +0 -7
- data/spec/lib/rack/consumer/http_long_poll_spec.rb +0 -30
- data/spec/lib/rack/consumer_spec.rb +0 -19
- data/spec/lib/rack/ping_spec.rb +0 -28
- data/spec/lib/rack/publisher_spec.rb +0 -34
- data/spec/lib/server/app_spec.rb +0 -15
- data/spec/lib/server/channel_spec.rb +0 -178
- data/spec/lib/server/message_buffer_spec.rb +0 -148
- data/spec/lib/server/publisher_spec.rb +0 -91
- data/spec/lib/server/redis_spec.rb +0 -13
- data/spec/lib/server/subscriber_spec.rb +0 -70
- data/spec/spec_helper.rb +0 -84
data/package.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "firehose",
|
|
3
|
-
"version": "1.3.3",
|
|
4
|
-
"main": "lib/assets/javascripts/firehose.js.coffee",
|
|
5
|
-
"devDependencies": {
|
|
6
|
-
"coffee-script": "*",
|
|
7
|
-
"jasmine-jquery": "git://github.com/velesin/jasmine-jquery.git",
|
|
8
|
-
"karma": "~0.12.16",
|
|
9
|
-
"karma-chrome-launcher": "~0.1.4",
|
|
10
|
-
"karma-coffee-preprocessor": "~0.2.1",
|
|
11
|
-
"karma-jasmine": "~0.2.0",
|
|
12
|
-
"karma-junit-reporter": "^0.2.2",
|
|
13
|
-
"karma-phantomjs-launcher": "^0.1.4",
|
|
14
|
-
"karma-safari-launcher": "*",
|
|
15
|
-
"karma-sprockets-mincer": "0.1.2"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
module IntegrationTestHelper
|
|
4
|
-
def start_server
|
|
5
|
-
@server_pid = fork do
|
|
6
|
-
Firehose::Server::App.new(:server => server, :port => uri.port).start
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
# Need to give the server a chance to boot up.
|
|
10
|
-
sleep 1
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def stop_server
|
|
14
|
-
Process.kill 'INT', @server_pid
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# Let isn't allowed in before(:all)
|
|
19
|
-
def uri
|
|
20
|
-
Firehose::URI
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'integrations/integration_test_helper'
|
|
3
|
-
require 'json'
|
|
4
|
-
|
|
5
|
-
shared_examples_for 'Firehose::Rack::App' do
|
|
6
|
-
include EM::TestHelper
|
|
7
|
-
include IntegrationTestHelper
|
|
8
|
-
|
|
9
|
-
before(:all) do
|
|
10
|
-
Firehose::Client::Producer::Http.adapter = :em_http
|
|
11
|
-
start_server
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
after(:all) do
|
|
15
|
-
Firehose::Client::Producer::Http.adapter = nil
|
|
16
|
-
stop_server
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
before(:each) { WebMock.disable! }
|
|
20
|
-
after(:each) { WebMock.enable! }
|
|
21
|
-
|
|
22
|
-
let(:app) { Firehose::Rack::App.new }
|
|
23
|
-
let(:messages) { (1..200).map{|n| "msg-#{n}" } }
|
|
24
|
-
let(:channel) { "/firehose/integration/#{Time.now.to_i}" }
|
|
25
|
-
let(:http_url) { "http://#{uri.host}:#{uri.port}#{channel}" }
|
|
26
|
-
let(:http_multi_url) { "http://#{uri.host}:#{uri.port}/channels@firehose" }
|
|
27
|
-
let(:ws_url) { "ws://#{uri.host}:#{uri.port}#{channel}" }
|
|
28
|
-
let(:multiplex_channels) { ["/foo/bar", "/bar/baz", "/baz/quux"] }
|
|
29
|
-
let(:subscription_query) { multiplex_channels.map{|c| "#{c}!0"}.join(",") }
|
|
30
|
-
|
|
31
|
-
it "supports pub-sub http and websockets" do
|
|
32
|
-
# Setup variables that we'll use after we turn off EM to validate our
|
|
33
|
-
# test assertions.
|
|
34
|
-
outgoing, received = messages.dup, Hash.new{|h,k| h[k] = []}
|
|
35
|
-
|
|
36
|
-
# Our WS and Http clients call this when they have received their messages to determine
|
|
37
|
-
# when to turn off EM and make the test assertion at the very bottom.
|
|
38
|
-
succeed = Proc.new do
|
|
39
|
-
# TODO: For some weird reason the `add_timer` call causes up to 20 seconds of delay after
|
|
40
|
-
# the test finishes running. However, without it the test will randomly fail with a
|
|
41
|
-
# "Redis disconnected" error.
|
|
42
|
-
em.add_timer(1) { em.stop } if received.values.all?{|arr| arr.size == messages.size }
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Setup a publisher
|
|
46
|
-
publish = Proc.new do
|
|
47
|
-
Firehose::Client::Producer::Http.new.publish(outgoing.shift).to(channel, buffer_size: rand(100)) do
|
|
48
|
-
# The random timer ensures that sometimes the clients will be behind
|
|
49
|
-
# and sometimes they will be caught up.
|
|
50
|
-
EM::add_timer(rand*0.005) { publish.call } unless outgoing.empty?
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Lets have an HTTP Long poll client
|
|
55
|
-
http_long_poll = Proc.new do |cid, last_sequence|
|
|
56
|
-
http = EM::HttpRequest.new(http_url).get(:query => {'last_message_sequence' => last_sequence})
|
|
57
|
-
http.errback { em.stop }
|
|
58
|
-
http.callback do
|
|
59
|
-
frame = JSON.parse(http.response, :symbolize_names => true)
|
|
60
|
-
received[cid] << frame[:message]
|
|
61
|
-
if received[cid].size < messages.size
|
|
62
|
-
# Add some jitter so the clients aren't syncronized
|
|
63
|
-
EM::add_timer(rand*0.001) { http_long_poll.call cid, frame[:last_sequence] }
|
|
64
|
-
else
|
|
65
|
-
succeed.call cid
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# And test a web socket client too, at the same time.
|
|
71
|
-
websocket = Proc.new do |cid|
|
|
72
|
-
ws = Faye::WebSocket::Client.new(ws_url)
|
|
73
|
-
|
|
74
|
-
ws.onopen = lambda do |event|
|
|
75
|
-
ws.send('{"message_sequence":0}')
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
ws.onmessage = lambda do |event|
|
|
79
|
-
frame = JSON.parse(event.data, :symbolize_names => true)
|
|
80
|
-
received[cid] << frame[:message]
|
|
81
|
-
succeed.call cid unless received[cid].size < messages.size
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
ws.onclose = lambda do |event|
|
|
85
|
-
ws = nil
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
ws.onerror = lambda do |event|
|
|
89
|
-
raise 'ws failed' + "\n" + event.inspect
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Great, we have all the pieces in order, lets run this thing in the reactor.
|
|
94
|
-
em 180 do
|
|
95
|
-
# Start the clients.
|
|
96
|
-
websocket.call(1)
|
|
97
|
-
websocket.call(2)
|
|
98
|
-
http_long_poll.call(3)
|
|
99
|
-
http_long_poll.call(4)
|
|
100
|
-
|
|
101
|
-
# Wait a sec to let our clients set up.
|
|
102
|
-
em.add_timer(1){ publish.call }
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# When EM stops, these assertions will be made.
|
|
106
|
-
expect(received.size).to eql(4)
|
|
107
|
-
received.each_value do |arr|
|
|
108
|
-
expect(arr.size).to eql(messages.size)
|
|
109
|
-
expect(arr.sort).to eql(messages.sort)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
it "supports channel multiplexing for http_long_poll and websockets" do
|
|
114
|
-
# Setup variables that we'll use after we turn off EM to validate our
|
|
115
|
-
# test assertions.
|
|
116
|
-
outgoing, received = messages.dup, Hash.new{|h,k| h[k] = []}
|
|
117
|
-
|
|
118
|
-
# Our WS and Http clients call this when they have received their messages to determine
|
|
119
|
-
# when to turn off EM and make the test assertion at the very bottom.
|
|
120
|
-
succeed = Proc.new do
|
|
121
|
-
# TODO: For some weird reason the `add_timer` call causes up to 20 seconds of delay after
|
|
122
|
-
# the test finishes running. However, without it the test will randomly fail with a
|
|
123
|
-
# "Redis disconnected" error.
|
|
124
|
-
em.add_timer(1) { em.stop } if received.values.all?{|arr| arr.size == messages.size }
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Lets have an HTTP Long poll client using channel multiplexing
|
|
128
|
-
multiplexed_http_long_poll = Proc.new do |cid, last_sequence|
|
|
129
|
-
http = EM::HttpRequest.new(http_multi_url).get(:query => {'subscribe' => subscription_query})
|
|
130
|
-
|
|
131
|
-
http.errback { em.stop }
|
|
132
|
-
http.callback do
|
|
133
|
-
frame = JSON.parse(http.response, :symbolize_names => true)
|
|
134
|
-
received[cid] << frame[:message]
|
|
135
|
-
if received[cid].size < messages.size
|
|
136
|
-
# Add some jitter so the clients aren't syncronized
|
|
137
|
-
EM::add_timer(rand*0.001) { multiplexed_http_long_poll.call cid, frame[:last_sequence] }
|
|
138
|
-
else
|
|
139
|
-
succeed.call cid
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Test multiplexed web socket client
|
|
145
|
-
outgoing = messages.dup
|
|
146
|
-
publish_multi = Proc.new do
|
|
147
|
-
msg = outgoing.shift
|
|
148
|
-
chan = multiplex_channels[rand(multiplex_channels.size)]
|
|
149
|
-
Firehose::Client::Producer::Http.new.publish(msg).to(chan) do
|
|
150
|
-
EM::add_timer(rand*0.005) { publish_multi.call } unless outgoing.empty?
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
multiplexed_websocket = Proc.new do |cid|
|
|
155
|
-
ws = Faye::WebSocket::Client.new("ws://#{uri.host}:#{uri.port}/channels@firehose?subscribe=#{subscription_query}")
|
|
156
|
-
|
|
157
|
-
ws.onmessage = lambda do |event|
|
|
158
|
-
frame = JSON.parse(event.data, :symbolize_names => true)
|
|
159
|
-
received[cid] << frame[:message]
|
|
160
|
-
succeed.call cid unless received[cid].size < messages.size
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
ws.onclose = lambda do |event|
|
|
164
|
-
ws = nil
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
ws.onerror = lambda do |event|
|
|
168
|
-
raise 'ws failed' + "\n" + event.inspect
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
em 180 do
|
|
173
|
-
# Start the clients.
|
|
174
|
-
multiplexed_http_long_poll.call(5)
|
|
175
|
-
multiplexed_http_long_poll.call(6)
|
|
176
|
-
multiplexed_websocket.call(7)
|
|
177
|
-
multiplexed_websocket.call(8)
|
|
178
|
-
|
|
179
|
-
# Wait a sec to let our clients set up.
|
|
180
|
-
em.add_timer(1){ publish_multi.call }
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
# When EM stops, these assertions will be made.
|
|
184
|
-
expect(received.size).to eql(4)
|
|
185
|
-
received.each_value do |arr|
|
|
186
|
-
expect(arr.size).to be <= messages.size
|
|
187
|
-
# expect(arr.sort).to eql(messages.sort)
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
it "returns 400 error for long-polling when using http long polling and sequence header is < 0" do
|
|
193
|
-
em 5 do
|
|
194
|
-
http = EM::HttpRequest.new(http_url).get(:query => {'last_message_sequence' => -1})
|
|
195
|
-
http.errback { |e| raise e.inspect }
|
|
196
|
-
http.callback do
|
|
197
|
-
expect(http.response_header.status).to eql(400)
|
|
198
|
-
em.stop
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
describe 'Firehose.Consumer', ->
|
|
2
|
-
beforeEach ->
|
|
3
|
-
@klass = Firehose.Consumer
|
|
4
|
-
@instance = new @klass uri: 'test_uri', okInterval: 9999 # Stop crazy ajax loops during tests
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
#= Specs ===
|
|
8
|
-
|
|
9
|
-
describe 'new instance', ->
|
|
10
|
-
describe '@config', ->
|
|
11
|
-
beforeEach -> @subj = @instance.config
|
|
12
|
-
|
|
13
|
-
for callback in ['message','error','connected','disconnected','failed']
|
|
14
|
-
it "has a .#{callback} function", ->
|
|
15
|
-
expect( @subj[callback] ).toEqual jasmine.any Function
|
|
16
|
-
|
|
17
|
-
it 'has a params object', ->
|
|
18
|
-
expect( @subj.params ).toEqual jasmine.any Object
|
|
19
|
-
|
|
20
|
-
it 'has a parse function', ->
|
|
21
|
-
expect( @subj.parse ).toEqual jasmine.any Function
|
|
22
|
-
|
|
23
|
-
describe '#connect', ->
|
|
24
|
-
it 'runs without error', -> @instance.connect()
|
|
25
|
-
|
|
26
|
-
describe 'after #connect called', ->
|
|
27
|
-
beforeEach -> @instance.connect()
|
|
28
|
-
|
|
29
|
-
it 'creates a @transport instance', ->
|
|
30
|
-
expect( @instance.transport ).toEqual jasmine.any Firehose.Transport
|
|
31
|
-
|
|
32
|
-
describe '#stop', ->
|
|
33
|
-
describe 'when .connect called earlier', ->
|
|
34
|
-
beforeEach ->
|
|
35
|
-
@instance.connect()
|
|
36
|
-
sinon.spy @instance.transport, 'stop'
|
|
37
|
-
|
|
38
|
-
it 'calls stop once on @transport', ->
|
|
39
|
-
@instance.stop()
|
|
40
|
-
expect( @instance.transport.stop.calledOnce ).toBe true
|
|
41
|
-
|
|
42
|
-
it 'stops the upgrade timeout', ->
|
|
43
|
-
@instance.stop()
|
|
44
|
-
expect( @instance.upgradeTimeout? ).toBe false
|
|
45
|
-
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
describe 'Firehose.LongPoll', ->
|
|
2
|
-
beforeEach ->
|
|
3
|
-
@klass = Firehose.LongPoll
|
|
4
|
-
@instance = new @klass()
|
|
5
|
-
|
|
6
|
-
afterEach -> @instance.stop()
|
|
7
|
-
|
|
8
|
-
#= Specs ===
|
|
9
|
-
|
|
10
|
-
it '.ieSupported returns a Boolean', ->
|
|
11
|
-
expect( typeof @klass.ieSupported() ).toBe 'boolean'
|
|
12
|
-
|
|
13
|
-
it '.supported returns a Boolean', ->
|
|
14
|
-
expect( typeof @klass.supported() ).toBe 'boolean'
|
|
15
|
-
|
|
16
|
-
describe '#stop', ->
|
|
17
|
-
beforeEach ->
|
|
18
|
-
@instance._lastRequest = $.ajax()
|
|
19
|
-
@instance._lastPingRequest = $.ajax()
|
|
20
|
-
|
|
21
|
-
it 'aborts the last request', ->
|
|
22
|
-
sinon.spy @instance._lastRequest, 'abort'
|
|
23
|
-
Object.freeze @instance # Prevents the delete call from modifying the request object, so we can verify the call
|
|
24
|
-
@instance.stop()
|
|
25
|
-
expect( @instance._lastRequest.abort.called ).toBe true
|
|
26
|
-
|
|
27
|
-
it 'deletes reference to the last request', ->
|
|
28
|
-
@instance.stop()
|
|
29
|
-
expect( @instance._lastRequest? ).toBe false
|
|
30
|
-
|
|
31
|
-
it 'aborts the last ping request', ->
|
|
32
|
-
sinon.spy @instance._lastPingRequest, 'abort'
|
|
33
|
-
Object.freeze @instance # Prevents the delete call from modifying the request object, so we can verify the call
|
|
34
|
-
@instance.stop()
|
|
35
|
-
expect( @instance._lastPingRequest.abort.called ).toBe true
|
|
36
|
-
|
|
37
|
-
it 'deletes reference to the last ping request', ->
|
|
38
|
-
@instance.stop()
|
|
39
|
-
expect( @instance._lastPingRequest? ).toBe false
|
|
40
|
-
|
|
41
|
-
describe 'callbacks', ->
|
|
42
|
-
for callback in ['_request','_success','_ping','_error']
|
|
43
|
-
it "has a #{callback} callback function", ->
|
|
44
|
-
expect( @instance[callback] ).toEqual jasmine.any Function
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
describe 'Firehose.MultiplexedConsumer', ->
|
|
2
|
-
|
|
3
|
-
beforeEach ->
|
|
4
|
-
@receivedMessages = []
|
|
5
|
-
|
|
6
|
-
@instance = new Firehose.MultiplexedConsumer
|
|
7
|
-
uri: '/'
|
|
8
|
-
okInterval: 9999 # Stop crazy ajax loops during tests
|
|
9
|
-
channels:
|
|
10
|
-
"/foo":
|
|
11
|
-
last_sequence: 0
|
|
12
|
-
message: (msg) =>
|
|
13
|
-
@receivedMessages.push msg
|
|
14
|
-
"/bar":
|
|
15
|
-
last_sequence: 10
|
|
16
|
-
message: (msg) =>
|
|
17
|
-
@receivedMessages.push msg
|
|
18
|
-
|
|
19
|
-
@instance.connected = ->
|
|
20
|
-
true
|
|
21
|
-
|
|
22
|
-
@instance.transport =
|
|
23
|
-
subscribe: (chan, opts) ->
|
|
24
|
-
true
|
|
25
|
-
unsubscribe: (chan, opts) ->
|
|
26
|
-
true
|
|
27
|
-
|
|
28
|
-
#= Specs ===
|
|
29
|
-
|
|
30
|
-
describe 'new instance', ->
|
|
31
|
-
it "receives only messages from channels it is currently subscribed to", ->
|
|
32
|
-
for i in [1..5]
|
|
33
|
-
@instance.message({channel: "/foo", message: JSON.stringify { message: "msg-#{i}" } })
|
|
34
|
-
for i in [6..10]
|
|
35
|
-
@instance.message({channel: "/bar", message: JSON.stringify { message: "msg-#{i}" } })
|
|
36
|
-
|
|
37
|
-
@instance.unsubscribe("/foo")
|
|
38
|
-
|
|
39
|
-
for i in [11..15]
|
|
40
|
-
@instance.message({channel: "/foo", message: JSON.stringify { message: "msg-#{i}" } })
|
|
41
|
-
for i in [16..20]
|
|
42
|
-
@instance.message({channel: "/bar", message: JSON.stringify { message: "msg-#{i}" } })
|
|
43
|
-
|
|
44
|
-
expect(@receivedMessages).toEqual(
|
|
45
|
-
{message: "msg-#{i}"} for i in [1,2,3,4,5,6,7,8,9,10,16,17,18,19,20]
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
describe "dynamic subscription handling", ->
|
|
49
|
-
it "subscribes & unsubscribes to a channel dynamically", ->
|
|
50
|
-
messages = []
|
|
51
|
-
|
|
52
|
-
@instance.message({channel: "/dynamic", message: JSON.stringify { message: "hello 1" }})
|
|
53
|
-
@instance.message({channel: "/dynamic", message: JSON.stringify { message: "hello 2" }})
|
|
54
|
-
|
|
55
|
-
expect(messages).toEqual([])
|
|
56
|
-
|
|
57
|
-
@instance.subscribe "/dynamic",
|
|
58
|
-
message: (msg) ->
|
|
59
|
-
messages.push(msg)
|
|
60
|
-
|
|
61
|
-
@instance.message({channel: "/dynamic", message: JSON.stringify { message: "hello 3" }})
|
|
62
|
-
@instance.message({channel: "/dynamic", message: JSON.stringify { message: "hello 4" }})
|
|
63
|
-
|
|
64
|
-
@instance.unsubscribe "/dynamic"
|
|
65
|
-
|
|
66
|
-
@instance.message({channel: "/dynamic", message: JSON.stringify { message: "hello 5" }})
|
|
67
|
-
@instance.message({channel: "/dynamic", message: JSON.stringify { message: "hello 6" }})
|
|
68
|
-
|
|
69
|
-
expect(messages).toEqual([
|
|
70
|
-
{message: "hello 3"},
|
|
71
|
-
{message: "hello 4"},
|
|
72
|
-
])
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
describe 'Firehose.Transport', ->
|
|
2
|
-
beforeEach ->
|
|
3
|
-
@klass = Firehose.Transport
|
|
4
|
-
@instance = new @klass()
|
|
5
|
-
|
|
6
|
-
it '.supported returns false', -> expect( @klass.supported() ).toBe false
|
|
7
|
-
it '.name throws error', -> expect( -> @instance.name ).toThrow()
|
|
8
|
-
it '.stop throws error', -> expect( -> @instance.stop ).toThrow()
|
|
9
|
-
|
|
10
|
-
describe 'constructor', ->
|
|
11
|
-
it 'returns an instance', -> expect( @instance ).toEqual jasmine.any @klass
|
|
12
|
-
it 'sets the @config object', -> expect( @instance.config ).toEqual jasmine.any Object
|
|
13
|
-
it 'sets a retry delay', -> expect( @instance._retryDelay ).toEqual jasmine.any Number
|
|
14
|
-
|
|
15
|
-
describe '#connect', ->
|
|
16
|
-
describe 'when given a 500 delay', ->
|
|
17
|
-
beforeEach ->
|
|
18
|
-
@clock = sinon.useFakeTimers()
|
|
19
|
-
sinon.stub @instance, '_request'
|
|
20
|
-
@instance.connect 500
|
|
21
|
-
|
|
22
|
-
afterEach -> @clock.restore()
|
|
23
|
-
|
|
24
|
-
it 'does not call _request before the given delay', ->
|
|
25
|
-
@clock.tick 499
|
|
26
|
-
expect( @instance._request.called ).toBe false
|
|
27
|
-
|
|
28
|
-
it 'calls _request after the given delay', ->
|
|
29
|
-
@clock.tick 600
|
|
30
|
-
expect( @instance._request.called ).toBe true
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
describe 'Firehose.WebSocket', ->
|
|
2
|
-
beforeEach ->
|
|
3
|
-
@klass = Firehose.WebSocket
|
|
4
|
-
@instance = new @klass()
|
|
5
|
-
|
|
6
|
-
#= Specs ===
|
|
7
|
-
|
|
8
|
-
it '.name returns a string', -> expect( @instance.name() ).toEqual jasmine.any String
|
|
9
|
-
it '.ieSupported returns a Boolean', -> expect( typeof @klass.ieSupported() ).toBe 'boolean'
|
|
10
|
-
it '.supported returns a Boolean', -> expect( typeof @klass.supported() ).toBe 'boolean'
|
|
11
|
-
|
|
12
|
-
describe 'callbacks', ->
|
|
13
|
-
for callback in ['_request','_success','_ping','_error']
|
|
14
|
-
it "has a #{callback} callback function", ->
|
|
15
|
-
expect( @instance[callback] ).toEqual jasmine.any Function
|
|
16
|
-
|
|
17
|
-
describe '#stop', ->
|
|
18
|
-
beforeEach ->
|
|
19
|
-
# call _request directly so we don't have to wait for the setTimeout
|
|
20
|
-
# call to call _request in the next event loop iteration
|
|
21
|
-
@instance.connect( uri: '//localhost/test' )._request()
|
|
22
|
-
sinon.spy @instance.socket, 'close'
|
|
23
|
-
Object.freeze @instance # stop _cleanUp method deleting the socket
|
|
24
|
-
|
|
25
|
-
it 'closes the socket', ->
|
|
26
|
-
@instance.stop()
|
|
27
|
-
expect( @instance.socket.close.called ).toBe true
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
require( '/javascripts/vendor/json2.js' );
|
|
2
|
-
require( '/javascripts/vendor/sinon-1.7.3.js' );
|
|
3
|
-
require( '/javascripts/vendor/jquery-1.11.1.js');
|
|
4
|
-
require( '/javascripts/firehose/base.js' );
|
|
5
|
-
require( '/javascripts/firehose/transport.js' );
|
|
6
|
-
require( '/javascripts/firehose/web_socket.js' );
|
|
7
|
-
require( '/javascripts/firehose/long_poll.js' );
|
|
8
|
-
require( '/javascripts/firehose/consumer.js' );
|
|
9
|
-
|
|
10
|
-
//= Mock out jQuery / Zepto calls
|
|
11
|
-
window.$ = {
|
|
12
|
-
ajax : function(){
|
|
13
|
-
return {
|
|
14
|
-
abort : function (){}
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
ajaxSettings : {
|
|
19
|
-
xhr : function(){
|
|
20
|
-
return {
|
|
21
|
-
withCredentials : true
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
param : function(){
|
|
27
|
-
return { fake: 'params' }
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
supported : { }
|
|
31
|
-
};
|