firehose 1.2.20 → 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 +36 -0
- data/{LICENSE.txt → LICENSE} +6 -6
- data/README.md +389 -139
- 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 +931 -18
- 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 +40 -428
- data/.env.sample +0 -17
- data/.gitignore +0 -12
- data/.rbenv-version +0 -1
- data/.rspec +0 -1
- data/.travis.yml +0 -16
- data/Gemfile +0 -4
- data/Guardfile +0 -31
- data/Procfile +0 -1
- data/Procfile.dev +0 -4
- data/Rakefile +0 -53
- data/bin/firehose +0 -9
- data/config/evergreen.rb +0 -9
- data/config/rainbows.rb +0 -17
- data/firehose.gemspec +0 -49
- data/lib/assets/javascripts/firehose/base.js.coffee +0 -4
- data/lib/assets/javascripts/firehose/consumer.js.coffee +0 -70
- data/lib/assets/javascripts/firehose/core.js.coffee +0 -5
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +0 -149
- data/lib/assets/javascripts/firehose/transport.js.coffee +0 -42
- data/lib/assets/javascripts/firehose/version.js.coffee.erb +0 -2
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +0 -102
- data/lib/assets/javascripts/firehose.js.coffee +0 -3
- data/lib/assets/javascripts/vendor/json2.js +0 -487
- data/lib/firehose/assets.rb +0 -56
- data/lib/firehose/cli.rb +0 -78
- data/lib/firehose/client/consumer.rb +0 -94
- data/lib/firehose/client/producer.rb +0 -108
- 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 -86
- data/lib/firehose/rack/consumer/web_socket.rb +0 -95
- data/lib/firehose/rack/consumer.rb +0 -39
- data/lib/firehose/rack/ping.rb +0 -88
- data/lib/firehose/rack/publisher.rb +0 -53
- 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 -80
- data/lib/firehose/server/publisher.rb +0 -134
- data/lib/firehose/server/subscriber.rb +0 -51
- data/lib/firehose/version.rb +0 -4
- data/package.json +0 -5
- data/spec/integrations/integration_test_helper.rb +0 -22
- data/spec/integrations/rainbows_spec.rb +0 -7
- data/spec/integrations/shared_examples.rb +0 -120
- 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/transport_spec.coffee +0 -32
- data/spec/javascripts/firehose/websocket_spec.coffee +0 -25
- data/spec/javascripts/helpers/spec_helper.js +0 -30
- data/spec/javascripts/support/sinon-1.7.3.js +0 -4290
- 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 -12
- 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 -164
- data/spec/lib/server/publisher_spec.rb +0 -84
- data/spec/lib/server/subscriber_spec.rb +0 -69
- data/spec/spec_helper.rb +0 -77
data/spec/lib/assets_spec.rb
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'sprockets'
|
|
3
|
-
|
|
4
|
-
describe Firehose::Assets do
|
|
5
|
-
describe ".path" do
|
|
6
|
-
it "should have root path" do
|
|
7
|
-
Firehose::Assets.path('poop').should == File.expand_path('../../../lib/assets/poop', __FILE__)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
it "should accept folders" do
|
|
11
|
-
Firehose::Assets.path('poop').should == File.join(Firehose::Assets.path, 'poop')
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
describe "Sprockets.configure" do
|
|
16
|
-
it "should configure environment" do
|
|
17
|
-
env = Firehose::Assets::Sprockets.configure Sprockets::Environment.new
|
|
18
|
-
env.paths.should include(Firehose::Assets.path('javascripts'))
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
describe "Sprockets.javascript" do
|
|
23
|
-
let(:js_spot_checks) do
|
|
24
|
-
[
|
|
25
|
-
"Firehose.Transport",
|
|
26
|
-
"Firehose.version",
|
|
27
|
-
"global.Firehose || (global.Firehose = {})",
|
|
28
|
-
"window.Firehose || (window.Firehose = {})"
|
|
29
|
-
]
|
|
30
|
-
end
|
|
31
|
-
it "should compile javascript" do
|
|
32
|
-
Firehose::Assets::Sprockets.javascript.should include(*js_spot_checks)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Firehose::Client::Consumer::WebSocket do
|
|
4
|
-
context "transport" do
|
|
5
|
-
# Transport for Firehose::Client::App class is tested via the spec/integrations suite.
|
|
6
|
-
end
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
describe Firehose::Client::Consumer::HttpLongPoll do
|
|
10
|
-
context "transport" do
|
|
11
|
-
# Transport for Firehose::Client::App class is tested via the spec/integrations suite.
|
|
12
|
-
end
|
|
13
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Firehose::Client::Producer::Http do
|
|
4
|
-
let(:channel) { "/channel-awesome" }
|
|
5
|
-
let(:url) { "#{Firehose::URI}#{channel}"}
|
|
6
|
-
let(:publish_stub) { stub_request(:put, url) }
|
|
7
|
-
let(:message) { "hey dude" }
|
|
8
|
-
|
|
9
|
-
before(:all) do
|
|
10
|
-
Firehose::Client::Producer::Http.adapter = :net_http
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
after(:all) do
|
|
14
|
-
Firehose::Client::Producer::Http.adapter = nil
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
it "should publish message to channel" do
|
|
18
|
-
publish_stub.to_return(:body => "", :status => 202)
|
|
19
|
-
|
|
20
|
-
Firehose::Client::Producer::Http.new.publish(message).to(channel)
|
|
21
|
-
WebMock.should have_requested(:put, url).with { |req| req.body == message }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
context 'prefix is specified in URI' do
|
|
25
|
-
let(:firehose_uri) {"#{Firehose::URI}/prefix"}
|
|
26
|
-
let(:url) { "#{firehose_uri}#{channel}"}
|
|
27
|
-
|
|
28
|
-
it "should publish message to channel" do
|
|
29
|
-
publish_stub.to_return(:body => "", :status => 202)
|
|
30
|
-
|
|
31
|
-
Firehose::Client::Producer::Http.new(firehose_uri).publish(message).to(channel)
|
|
32
|
-
WebMock.should have_requested(:put, url).with { |req| req.body == message }
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
it "should publish message to channel with expiry headers" do
|
|
37
|
-
publish_stub.to_return(:body => "", :status => 202)
|
|
38
|
-
ttl = 20
|
|
39
|
-
|
|
40
|
-
Firehose::Client::Producer::Http.new.publish(message).to(channel, :ttl => ttl)
|
|
41
|
-
WebMock.should have_requested(:put, url).with { |req| req.body == message and req.headers['Cache-Control'] == "max-age=#{ttl}" }
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
describe "connection error handling" do
|
|
45
|
-
it "should raise PublishError if not 201" do
|
|
46
|
-
publish_stub.to_return(:body => "", :status => 500)
|
|
47
|
-
|
|
48
|
-
lambda{
|
|
49
|
-
Firehose::Client::Producer::Http.new.publish(message).to(channel)
|
|
50
|
-
}.should raise_exception(Firehose::Client::Producer::Http::PublishError)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
it "should use .error_handler if not 201" do
|
|
54
|
-
publish_stub.to_return(:body => "", :status => 500)
|
|
55
|
-
|
|
56
|
-
producer = Firehose::Client::Producer::Http.new
|
|
57
|
-
producer.on_error do |e|
|
|
58
|
-
e.message.should =~ /could not publish.+to/i
|
|
59
|
-
end
|
|
60
|
-
producer.publish(message).to(channel)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
it "should raise TimeoutError if timed out" do
|
|
64
|
-
publish_stub.to_timeout
|
|
65
|
-
|
|
66
|
-
lambda{
|
|
67
|
-
Firehose::Client::Producer::Http.new.publish(message).to(channel)
|
|
68
|
-
}.should raise_exception(Firehose::Client::Producer::Http::TimeoutError)
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
data/spec/lib/firehose_spec.rb
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Firehose::Rack::Consumer::HttpLongPoll do
|
|
4
|
-
context "transport" do
|
|
5
|
-
# Transport for Firehose::Rack::App class is tested via the spec/integrations suite.
|
|
6
|
-
end
|
|
7
|
-
context "configuration" do
|
|
8
|
-
it "should have #timeout" do
|
|
9
|
-
Firehose::Rack::Consumer::HttpLongPoll.new(200).timeout.should == 200
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'rack/test'
|
|
3
|
-
|
|
4
|
-
describe Firehose::Rack::Consumer, :type => :request do
|
|
5
|
-
include Rack::Test::Methods
|
|
6
|
-
let(:app) { Firehose::Rack::Consumer.new }
|
|
7
|
-
let(:path) { "/test/path/#{Time.now.to_i}" }
|
|
8
|
-
|
|
9
|
-
it "should have Content-Length on OPTIONS request"
|
|
10
|
-
it "should have Content-Length on GET request"
|
|
11
|
-
|
|
12
|
-
context "configuration" do
|
|
13
|
-
let(:app) { Firehose::Rack::Consumer }
|
|
14
|
-
|
|
15
|
-
it "should configure long polling timeout" do
|
|
16
|
-
app.new{ |a| a.http_long_poll.timeout = 300 }.http_long_poll.timeout.should == 300
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
data/spec/lib/rack/ping_spec.rb
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'rack/test'
|
|
3
|
-
require 'async_rack_test'
|
|
4
|
-
|
|
5
|
-
describe Firehose::Rack::Ping, :type => :request do
|
|
6
|
-
include AsyncRackTest::Methods
|
|
7
|
-
let(:app) { Firehose::Rack::Ping.new dummy_redis }
|
|
8
|
-
let(:path) { "/test/path/#{Time.now.to_i}" }
|
|
9
|
-
let(:deferrable) { EM::DefaultDeferrable.new }
|
|
10
|
-
let(:dummy_redis) { double 'redis', :set => deferrable, :get => deferrable, :expire => deferrable }
|
|
11
|
-
|
|
12
|
-
context 'redis is available' do
|
|
13
|
-
before { deferrable.succeed Firehose::Rack::Ping::PingCheck::TEST_VALUE }
|
|
14
|
-
|
|
15
|
-
it "should return 200" do
|
|
16
|
-
ahead path
|
|
17
|
-
last_response.status.should == 200
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
context 'redis is not available' do
|
|
22
|
-
before { deferrable.fail 'some error' }
|
|
23
|
-
it "should return 500" do
|
|
24
|
-
ahead path
|
|
25
|
-
last_response.status.should == 500
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'rack/test'
|
|
3
|
-
require 'async_rack_test'
|
|
4
|
-
|
|
5
|
-
describe Firehose::Rack::Publisher, :type => :request do
|
|
6
|
-
include AsyncRackTest::Methods
|
|
7
|
-
let(:app) { Firehose::Rack::Publisher.new }
|
|
8
|
-
let(:path) { "/test/path/#{Time.now.to_i}" }
|
|
9
|
-
let(:deferrable) { EM::DefaultDeferrable.new }
|
|
10
|
-
|
|
11
|
-
context 'publishing is successful' do
|
|
12
|
-
before { deferrable.succeed }
|
|
13
|
-
|
|
14
|
-
it "should return 202" do
|
|
15
|
-
app.stub(:publisher => double('publisher', :publish => deferrable))
|
|
16
|
-
aput path, :body => "some nice little message"
|
|
17
|
-
last_response.status.should == 202
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it "should have Content-Length of zero" do
|
|
21
|
-
app.stub(:publisher => double('publisher', :publish => deferrable))
|
|
22
|
-
aput path, :body => "some nice little message"
|
|
23
|
-
last_response.headers['Content-Length'].should == '0'
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
it "should parse Cache-Control max-age" do
|
|
27
|
-
body = "howdy dude!"
|
|
28
|
-
ttl = '92'
|
|
29
|
-
|
|
30
|
-
Firehose::Server::Publisher.any_instance.stub(:publish).with(path, body, :ttl => ttl).and_return(deferrable)
|
|
31
|
-
aput path, body, 'HTTP_CACHE_CONTROL' => 'max-age=92'
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
data/spec/lib/server/app_spec.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Firehose::Rack::App do
|
|
4
|
-
context "transport" do
|
|
5
|
-
# Transport for Firehose::Rack::App class is tested via the spec/integrations suite.
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
context "configuration" do
|
|
9
|
-
let(:app) { Firehose::Rack::App }
|
|
10
|
-
|
|
11
|
-
it "should configure long polling timeout" do
|
|
12
|
-
app.new{ |a| a.consumer.http_long_poll.timeout = 300 }.consumer.http_long_poll.timeout.should == 300
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Firehose::Server::Channel do
|
|
4
|
-
include EM::TestHelper
|
|
5
|
-
|
|
6
|
-
let(:channel_key) { '/bears/are/mean' }
|
|
7
|
-
let(:channel) { Firehose::Server::Channel.new(channel_key, EM::Hiredis.connect, subscriber) }
|
|
8
|
-
let(:subscriber) { Firehose::Server::Subscriber.new(EM::Hiredis.connect) }
|
|
9
|
-
let(:message) { 'Raaaarrrrrr!!!!' }
|
|
10
|
-
let(:publisher) { Firehose::Server::Publisher.new }
|
|
11
|
-
|
|
12
|
-
describe "#next_message" do
|
|
13
|
-
it "should wait for message if message was not published before subscription" do
|
|
14
|
-
em do
|
|
15
|
-
channel.next_message.callback do |msg, seq|
|
|
16
|
-
msg.should == message
|
|
17
|
-
seq.should == 1
|
|
18
|
-
em.next_tick { em.stop }
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
publisher.publish(channel_key, message)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it "should return the latest message and sequence if no sequence is given" do
|
|
26
|
-
redis_exec 'lpush', "firehose:#{channel_key}:list", message
|
|
27
|
-
redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
|
|
28
|
-
|
|
29
|
-
em do
|
|
30
|
-
channel.next_message.callback do |msg, seq|
|
|
31
|
-
msg.should == message
|
|
32
|
-
seq.should == 100
|
|
33
|
-
|
|
34
|
-
# This must happen _after_ the callback runs in order to pass consistently.
|
|
35
|
-
em.next_tick { em.stop }
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
it "should wait for message if most recent sequence is given" do
|
|
41
|
-
redis_exec 'lpush', "firehose:#{channel_key}:list", message
|
|
42
|
-
redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
|
|
43
|
-
|
|
44
|
-
em 3 do
|
|
45
|
-
channel.next_message(100).callback do |msg, seq|
|
|
46
|
-
msg.should == message
|
|
47
|
-
seq.should == 101
|
|
48
|
-
em.next_tick { em.stop }
|
|
49
|
-
end.errback
|
|
50
|
-
|
|
51
|
-
publisher.publish(channel_key, message)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it "should wait for message if a future sequence is given" do
|
|
56
|
-
redis_exec 'lpush', "firehose:#{channel_key}:list", message
|
|
57
|
-
redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
|
|
58
|
-
|
|
59
|
-
em 3 do
|
|
60
|
-
channel.next_message(101).callback do |msg, seq|
|
|
61
|
-
msg.should == message
|
|
62
|
-
seq.should == 101
|
|
63
|
-
em.next_tick { em.stop }
|
|
64
|
-
end.errback
|
|
65
|
-
|
|
66
|
-
publisher.publish(channel_key, message)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it "should immediatly get a message if message sequence is behind and in list" do
|
|
71
|
-
messages = %w[a b c d e]
|
|
72
|
-
|
|
73
|
-
em 3 do
|
|
74
|
-
publish_messages(messages) do
|
|
75
|
-
channel.next_message(2).callback do |msg, seq|
|
|
76
|
-
msg.should == 'c'
|
|
77
|
-
seq.should == 3
|
|
78
|
-
|
|
79
|
-
# This must happen _after_ the callback runs in order to pass consistently.
|
|
80
|
-
em.next_tick { em.stop }
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
it "should get current message if sequence is really far behind in list" do
|
|
87
|
-
messages = ('aa'..'zz').to_a
|
|
88
|
-
|
|
89
|
-
em 3 do
|
|
90
|
-
publish_messages(messages) do
|
|
91
|
-
channel.next_message(2).callback do |msg, seq|
|
|
92
|
-
msg.should == messages.last
|
|
93
|
-
seq.should == messages.size
|
|
94
|
-
|
|
95
|
-
# This must happen _after_ the callback runs in order to pass consistently.
|
|
96
|
-
em.next_tick { em.stop }
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
context "a timeout is set" do
|
|
103
|
-
it "should timeout if message isn't published in time" do
|
|
104
|
-
redis_exec 'lpush', "firehose:#{channel_key}:list", message
|
|
105
|
-
redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
|
|
106
|
-
|
|
107
|
-
em 3 do
|
|
108
|
-
channel.next_message(100, :timeout => 1).callback do |msg, seq|
|
|
109
|
-
raise 'test failed'
|
|
110
|
-
end.errback do |e|
|
|
111
|
-
e.should == :timeout
|
|
112
|
-
em.next_tick { em.stop }
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
EM::add_timer(2) do
|
|
116
|
-
publisher.publish(channel_key, message)
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
it "should not timeout if message is published in time" do
|
|
122
|
-
redis_exec 'lpush', "firehose:#{channel_key}:list", message
|
|
123
|
-
redis_exec 'set', "firehose:#{channel_key}:sequence", '100'
|
|
124
|
-
|
|
125
|
-
em 3 do
|
|
126
|
-
d = channel.next_message(100, :timeout => 2).callback do |msg, seq|
|
|
127
|
-
msg.should == message
|
|
128
|
-
seq.should == 101
|
|
129
|
-
EM::add_timer(1) do
|
|
130
|
-
em.stop
|
|
131
|
-
end
|
|
132
|
-
end.errback do |e|
|
|
133
|
-
raise 'test failed'
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
d.should_not_receive(:fail)
|
|
137
|
-
|
|
138
|
-
EM::add_timer(1) do
|
|
139
|
-
publisher.publish(channel_key, message)
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# Publishes the given messages and executes the given block when finished.
|
|
148
|
-
def publish_messages(all_messages)
|
|
149
|
-
publish = Proc.new do |messages_to_publish|
|
|
150
|
-
msg = messages_to_publish.shift
|
|
151
|
-
publisher.publish(channel_key, msg).callback do
|
|
152
|
-
if messages_to_publish.empty?
|
|
153
|
-
# Publishing is done, proceed with the test
|
|
154
|
-
yield
|
|
155
|
-
else
|
|
156
|
-
# Publish the next message
|
|
157
|
-
publish.call messages_to_publish
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
publish.call all_messages.dup
|
|
163
|
-
end
|
|
164
|
-
end
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Firehose::Server::Publisher do
|
|
4
|
-
include EM::TestHelper
|
|
5
|
-
|
|
6
|
-
let(:publisher) { Firehose::Server::Publisher.new }
|
|
7
|
-
let(:channel_key) { "/firehose/publisher/test/#{Time.now.to_i}" }
|
|
8
|
-
let(:message) { "howdy friends!" }
|
|
9
|
-
|
|
10
|
-
it "should have 100 MAX_MESSAGES" do
|
|
11
|
-
Firehose::Server::Publisher::MAX_MESSAGES.should == 100
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it "should have 1 day TTL" do
|
|
15
|
-
Firehose::Server::Publisher::TTL.should == 86400
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
describe "#publish" do
|
|
19
|
-
it "should publish message change" do
|
|
20
|
-
em do
|
|
21
|
-
hiredis = EM::Hiredis.connect.pubsub
|
|
22
|
-
hiredis.subscribe "firehose:channel_updates"
|
|
23
|
-
hiredis.on(:message) {|_, msg|
|
|
24
|
-
msg.should == "#{channel_key}\n1\n#{message}"
|
|
25
|
-
em.next_tick { em.stop }
|
|
26
|
-
}
|
|
27
|
-
Firehose::Server::Publisher.new.publish channel_key, message
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
"\"'\r\t\n!@\#$%^&*()[]\v\f\a\b\e{}/=?+\\|".each_char do |char|
|
|
32
|
-
it "should publish messages with the '#{char.inspect}' character" do
|
|
33
|
-
msg = [char, message, char].join
|
|
34
|
-
em 1 do
|
|
35
|
-
Firehose::Server::Publisher.new.publish(channel_key, msg).callback { em.stop }
|
|
36
|
-
end
|
|
37
|
-
redis_exec('lpop', "firehose:#{channel_key}:list").should == msg
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it "should add message to list" do
|
|
42
|
-
em do
|
|
43
|
-
Firehose::Server::Publisher.new.publish(channel_key, message).callback { em.stop }
|
|
44
|
-
end
|
|
45
|
-
redis_exec('lpop', "firehose:#{channel_key}:list").should == message
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it "should limit list to MAX_MESSAGES messages" do
|
|
49
|
-
em do
|
|
50
|
-
Firehose::Server::Publisher::MAX_MESSAGES.times do |n|
|
|
51
|
-
publisher.publish(channel_key, message)
|
|
52
|
-
end
|
|
53
|
-
publisher.publish(channel_key, message).callback { em.stop }
|
|
54
|
-
end
|
|
55
|
-
redis_exec('llen', "firehose:#{channel_key}:list").should == Firehose::Server::Publisher::MAX_MESSAGES
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
it "should increment sequence" do
|
|
59
|
-
sequence_key = "firehose:#{channel_key}:sequence"
|
|
60
|
-
|
|
61
|
-
@done_counter = 0
|
|
62
|
-
redis_exec('get', sequence_key).should be_nil
|
|
63
|
-
em do
|
|
64
|
-
publisher.publish(channel_key, message).callback { @done_counter += 1; em.next_tick { em.stop } if @done_counter > 1 }
|
|
65
|
-
publisher.publish(channel_key, message).callback { @done_counter += 1; em.next_tick { em.stop } if @done_counter > 1 }
|
|
66
|
-
end
|
|
67
|
-
redis_exec('get', sequence_key).to_i.should == 2
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it "should set expirery on sequence and list keys" do
|
|
71
|
-
ttl = 78 # Why 78? Why not!
|
|
72
|
-
|
|
73
|
-
em do
|
|
74
|
-
publisher.publish(channel_key, message, :ttl => 78).callback do
|
|
75
|
-
# Allow for 1 second of slippage/delay
|
|
76
|
-
redis_exec('TTL', "firehose:#{channel_key}:sequence").should > (ttl- 1)
|
|
77
|
-
redis_exec('TTL', "firehose:#{channel_key}:list").should > (ttl - 1)
|
|
78
|
-
em.stop
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
end
|
|
84
|
-
end
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Firehose::Server::Subscriber do
|
|
4
|
-
include EM::TestHelper
|
|
5
|
-
|
|
6
|
-
let(:channel_key) { '/bears/are/mean' }
|
|
7
|
-
let(:subscriber) { Firehose::Server::Subscriber.new(EM::Hiredis.connect) }
|
|
8
|
-
let(:dummy_subscriber){ Firehose::Server::Subscriber.new(double('redis', :pubsub => double('pubsub', :subscribe => EM::DefaultDeferrable.new, :on => nil))) }
|
|
9
|
-
let(:message) { 'Raaaarrrrrr!!!!' }
|
|
10
|
-
let(:publisher) { Firehose::Server::Publisher.new }
|
|
11
|
-
|
|
12
|
-
describe "#subscribe" do
|
|
13
|
-
it "should add the deferrable to the subscriptions hash" do
|
|
14
|
-
deferrable = EM::DefaultDeferrable.new
|
|
15
|
-
dummy_subscriber.subscribe(channel_key, deferrable)
|
|
16
|
-
dummy_subscriber.send(:subscriptions)[channel_key].should == [deferrable]
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it "should call succeed on the deferrable when a message is published" do
|
|
20
|
-
deferrable = EM::DefaultDeferrable.new
|
|
21
|
-
deferrable.callback do |msg, sequence|
|
|
22
|
-
msg.should == message
|
|
23
|
-
sequence.should == 1 # The publisher is fresh, so the sequence ID will be 1.
|
|
24
|
-
em.next_tick { em.stop }
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
em do
|
|
28
|
-
subscriber.subscribe(channel_key, deferrable)
|
|
29
|
-
publisher.publish(channel_key, message)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it "shouldn't call succeed on the deferrable when a 2nd message is published" do
|
|
34
|
-
deferrable = EM::DefaultDeferrable.new
|
|
35
|
-
deferrable.should_receive(:succeed).with(message, 1) # The publisher is fresh, so the sequence ID will be 1.
|
|
36
|
-
deferrable.should_not_receive(:succeed).with('2nd message', 2)
|
|
37
|
-
em do
|
|
38
|
-
subscriber.subscribe(channel_key, deferrable)
|
|
39
|
-
publisher.publish(channel_key, message).callback do
|
|
40
|
-
publisher.publish(channel_key, '2nd message').callback do
|
|
41
|
-
em.stop
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
describe "#unsubscribe" do
|
|
50
|
-
it "should remove the deferrable from the subscriptions hash" do
|
|
51
|
-
deferrable = EM::DefaultDeferrable.new
|
|
52
|
-
dummy_subscriber.subscribe(channel_key, deferrable)
|
|
53
|
-
dummy_subscriber.unsubscribe(channel_key, deferrable)
|
|
54
|
-
dummy_subscriber.send(:subscriptions).has_key?(channel_key).should be_false
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
it "shouldn't call succeed on the deferrable when a message is published" do
|
|
58
|
-
deferrable = EM::DefaultDeferrable.new
|
|
59
|
-
deferrable.should_not_receive(:succeed).with(message, 1) # The publisher is fresh, so the sequence ID will be 1.
|
|
60
|
-
em do
|
|
61
|
-
subscriber.subscribe(channel_key, deferrable)
|
|
62
|
-
subscriber.unsubscribe(channel_key, deferrable)
|
|
63
|
-
publisher.publish(channel_key, message).callback do
|
|
64
|
-
em.stop
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
data/spec/spec_helper.rb
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
ENV['RACK_ENV'] ||= 'test'
|
|
2
|
-
|
|
3
|
-
require 'logger'
|
|
4
|
-
require 'em-http'
|
|
5
|
-
require 'faye/websocket'
|
|
6
|
-
require 'hiredis'
|
|
7
|
-
require 'firehose'
|
|
8
|
-
|
|
9
|
-
# We use both EM::Http and Net::Http in this test lib. When EM:Http is fired up
|
|
10
|
-
# we're usually hitting Rainbows! for integrations, and when Net::Http we want
|
|
11
|
-
# to mock that up.
|
|
12
|
-
require 'webmock/rspec'
|
|
13
|
-
WebMock.allow_net_connect!
|
|
14
|
-
|
|
15
|
-
module EM::TestHelper
|
|
16
|
-
# Run test inside of reactor.
|
|
17
|
-
def em(ttl=30, &block)
|
|
18
|
-
if block
|
|
19
|
-
# Run the block inside of a reactor
|
|
20
|
-
EM.run do
|
|
21
|
-
EM.add_timer(ttl) do
|
|
22
|
-
EM.stop
|
|
23
|
-
raise "Test timed-out"
|
|
24
|
-
end
|
|
25
|
-
block.call(EM)
|
|
26
|
-
end
|
|
27
|
-
else # or just grab em and go nuts.
|
|
28
|
-
EM
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
module Hiredis::TestHelper
|
|
34
|
-
def redis
|
|
35
|
-
@conn ||= begin
|
|
36
|
-
conn = Hiredis::Connection.new
|
|
37
|
-
conn.connect("127.0.0.1", 6379)
|
|
38
|
-
conn
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def reset_redis
|
|
43
|
-
redis_exec 'flushdb'
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def redis_exec(*tokens)
|
|
47
|
-
redis.write tokens
|
|
48
|
-
redis.read
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Configure RSpec runner. See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
53
|
-
RSpec.configure do |config|
|
|
54
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
|
55
|
-
config.run_all_when_everything_filtered = true
|
|
56
|
-
config.filter_run :focus
|
|
57
|
-
config.include Hiredis::TestHelper
|
|
58
|
-
config.before(:each) do
|
|
59
|
-
reset_redis
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Allow async responses to get through rack/lint
|
|
64
|
-
require 'rack/lint'
|
|
65
|
-
class Rack::Lint
|
|
66
|
-
def check_status_with_async(status)
|
|
67
|
-
check_status_without_async(status) unless status == -1
|
|
68
|
-
end
|
|
69
|
-
alias_method :check_status_without_async, :check_status
|
|
70
|
-
alias_method :check_status, :check_status_with_async
|
|
71
|
-
|
|
72
|
-
def check_content_type_with_async(status, headers)
|
|
73
|
-
check_content_type_without_async(status, headers) unless status == -1
|
|
74
|
-
end
|
|
75
|
-
alias_method :check_content_type_without_async, :check_content_type
|
|
76
|
-
alias_method :check_content_type, :check_content_type_with_async
|
|
77
|
-
end
|