faye 0.8.11 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of faye might be problematic. Click here for more details.

Files changed (73) hide show
  1. data/{History.txt → CHANGELOG.md} +126 -105
  2. data/README.md +36 -0
  3. data/lib/faye-browser-min.js +2 -1
  4. data/lib/faye-browser-min.js.map +1 -8
  5. data/lib/faye-browser.js +923 -607
  6. data/lib/faye.rb +11 -5
  7. data/lib/faye/adapters/rack_adapter.rb +80 -85
  8. data/lib/faye/engines/connection.rb +7 -9
  9. data/lib/faye/engines/memory.rb +1 -0
  10. data/lib/faye/engines/proxy.rb +7 -6
  11. data/lib/faye/mixins/deferrable.rb +15 -0
  12. data/lib/faye/mixins/logging.rb +11 -22
  13. data/lib/faye/mixins/publisher.rb +9 -20
  14. data/lib/faye/protocol/channel.rb +2 -1
  15. data/lib/faye/protocol/client.rb +70 -48
  16. data/lib/faye/protocol/envelope.rb +24 -0
  17. data/lib/faye/protocol/extensible.rb +7 -4
  18. data/lib/faye/protocol/publication.rb +1 -1
  19. data/lib/faye/protocol/server.rb +8 -11
  20. data/lib/faye/protocol/socket.rb +6 -4
  21. data/lib/faye/protocol/subscription.rb +1 -1
  22. data/lib/faye/transport/http.rb +20 -27
  23. data/lib/faye/transport/local.rb +5 -5
  24. data/lib/faye/transport/transport.rb +42 -12
  25. data/lib/faye/transport/web_socket.rb +71 -38
  26. metadata +169 -137
  27. checksums.yaml +0 -7
  28. data/README.rdoc +0 -83
  29. data/spec/browser.html +0 -45
  30. data/spec/encoding_helper.rb +0 -7
  31. data/spec/install.sh +0 -78
  32. data/spec/javascript/channel_spec.js +0 -15
  33. data/spec/javascript/client_spec.js +0 -729
  34. data/spec/javascript/dispatcher_spec.js +0 -122
  35. data/spec/javascript/engine/memory_spec.js +0 -7
  36. data/spec/javascript/engine_spec.js +0 -417
  37. data/spec/javascript/faye_spec.js +0 -34
  38. data/spec/javascript/grammar_spec.js +0 -66
  39. data/spec/javascript/node_adapter_spec.js +0 -314
  40. data/spec/javascript/publisher_spec.js +0 -27
  41. data/spec/javascript/server/connect_spec.js +0 -168
  42. data/spec/javascript/server/disconnect_spec.js +0 -121
  43. data/spec/javascript/server/extensions_spec.js +0 -60
  44. data/spec/javascript/server/handshake_spec.js +0 -145
  45. data/spec/javascript/server/integration_spec.js +0 -131
  46. data/spec/javascript/server/publish_spec.js +0 -85
  47. data/spec/javascript/server/subscribe_spec.js +0 -247
  48. data/spec/javascript/server/unsubscribe_spec.js +0 -245
  49. data/spec/javascript/server_spec.js +0 -121
  50. data/spec/javascript/transport_spec.js +0 -135
  51. data/spec/node.js +0 -55
  52. data/spec/phantom.js +0 -17
  53. data/spec/ruby/channel_spec.rb +0 -17
  54. data/spec/ruby/client_spec.rb +0 -741
  55. data/spec/ruby/engine/memory_spec.rb +0 -7
  56. data/spec/ruby/engine_examples.rb +0 -427
  57. data/spec/ruby/faye_spec.rb +0 -30
  58. data/spec/ruby/grammar_spec.rb +0 -68
  59. data/spec/ruby/publisher_spec.rb +0 -27
  60. data/spec/ruby/rack_adapter_spec.rb +0 -241
  61. data/spec/ruby/server/connect_spec.rb +0 -170
  62. data/spec/ruby/server/disconnect_spec.rb +0 -120
  63. data/spec/ruby/server/extensions_spec.rb +0 -68
  64. data/spec/ruby/server/handshake_spec.rb +0 -143
  65. data/spec/ruby/server/integration_spec.rb +0 -133
  66. data/spec/ruby/server/publish_spec.rb +0 -81
  67. data/spec/ruby/server/subscribe_spec.rb +0 -247
  68. data/spec/ruby/server/unsubscribe_spec.rb +0 -247
  69. data/spec/ruby/server_spec.rb +0 -121
  70. data/spec/ruby/transport_spec.rb +0 -136
  71. data/spec/spec_helper.rb +0 -11
  72. data/spec/testswarm +0 -42
  73. data/spec/thin_proxy.rb +0 -37
@@ -1,121 +0,0 @@
1
- JS.ENV.ServerSpec = JS.Test.describe("Server", function() { with(this) {
2
- before(function() { with(this) {
3
- this.engine = {}
4
- stub(Faye.Engine, "get").returns(engine)
5
- this.server = new Faye.Server()
6
- }})
7
-
8
- describe("#process", function() { with(this) {
9
- before(function() { with(this) {
10
- this.handshake = {channel: "/meta/handshake", data: "handshake"}
11
- this.connect = {channel: "/meta/connect", data: "connect"}
12
- this.disconnect = {channel: "/meta/disconnect", data: "disconnect"}
13
- this.subscribe = {channel: "/meta/subscribe", data: "subscribe"}
14
- this.unsubscribe = {channel: "/meta/unsubscribe", data: "unsubscribe"}
15
- this.publish = {channel: "/some/channel", data: "publish"}
16
-
17
- stub(engine, "interval", 0)
18
- stub(engine, "timeout", 60)
19
- }})
20
-
21
- it("returns an empty response for no messages", function() { with(this) {
22
- var response = null
23
- server.process([], false, function(r) { response = r})
24
- assertEqual( [], response )
25
- }})
26
-
27
- it("ignores invalid messages", function() { with(this) {
28
- var response = null
29
- server.process([{}, {channel: "invalid"}], false, function(r) { response = r})
30
- assertEqual([
31
- { successful: false,
32
- error: "405::Invalid channel"
33
- },
34
- { channel: "invalid",
35
- successful: false,
36
- error: "405:invalid:Invalid channel"
37
- }
38
- ], response)
39
- }})
40
-
41
- it("rejects unknown meta channels", function() { with(this) {
42
- var response = null
43
- server.process([{channel: "/meta/p"}], false, function(r) { response = r })
44
- assertEqual([
45
- { channel: "/meta/p",
46
- successful: false,
47
- error: "403:/meta/p:Forbidden channel"
48
- }
49
- ], response)
50
- }})
51
-
52
- it("routes single messages to appropriate handlers", function() { with(this) {
53
- expect(server, "handshake").given(handshake, false).yielding([{}])
54
- server.process(handshake, false, function() {})
55
- }})
56
-
57
- it("routes a list of messages to appropriate handlers", function() { with(this) {
58
- expect(server, "handshake").given(handshake, false).yielding([{}])
59
- expect(server, "connect").given(connect, false).yielding([{}])
60
- expect(server, "disconnect").given(disconnect, false).yielding([{}])
61
- expect(server, "subscribe").given(subscribe, false).yielding([{}])
62
- expect(server, "unsubscribe").given(unsubscribe, false).yielding([{}])
63
-
64
- expect(engine, "publish").given(handshake).exactly(0)
65
- expect(engine, "publish").given(connect).exactly(0)
66
- expect(engine, "publish").given(disconnect).exactly(0)
67
- expect(engine, "publish").given(subscribe).exactly(0)
68
- expect(engine, "publish").given(unsubscribe).exactly(0)
69
-
70
- expect(engine, "publish").given(publish)
71
-
72
- server.process([handshake, connect, disconnect, subscribe, unsubscribe, publish], false, function() {})
73
- }})
74
-
75
- describe("handshaking", function() { with(this) {
76
- before(function() { with(this) {
77
- expect(server, "handshake").given(handshake, false).yielding([{channel: "/meta/handshake", successful: true}])
78
- }})
79
-
80
- it("returns the handshake response with advice", function() { with(this) {
81
- server.process(handshake, false, function(response) {
82
- assertEqual([
83
- { channel: "/meta/handshake",
84
- successful: true,
85
- advice: {reconnect: "retry", interval: 0, timeout: 60000}
86
- }
87
- ], response)
88
- })
89
- }})
90
- }})
91
-
92
- describe("connecting for messages", function() { with(this) {
93
- before(function() { with(this) {
94
- this.messages = [{channel: "/a"}, {channel: "/b"}]
95
- expect(server, "connect").given(connect, false).yielding([messages])
96
- }})
97
-
98
- it("returns the new messages", function() { with(this) {
99
- server.process(connect, false, function(response) {
100
- assertEqual( messages, response )
101
- })
102
- }})
103
- }})
104
- }})
105
-
106
- describe("#flushConnection", function() { with(this) {
107
- before(function() { with(this) {
108
- this.message = {clientId: "fakeclientid"}
109
- }})
110
-
111
- it("flushes the connection when given one message", function() { with(this) {
112
- expect(engine, "flush").given("fakeclientid")
113
- server.flushConnection(message)
114
- }})
115
-
116
- it("flushes the connection when given a list of messages", function() { with(this) {
117
- expect(engine, "flush").given("fakeclientid")
118
- server.flushConnection([message])
119
- }})
120
- }})
121
- }})
@@ -1,135 +0,0 @@
1
- JS.ENV.TransportSpec = JS.Test.describe("Transport", function() { with(this) {
2
- before(function() { with(this) {
3
- this.client = {
4
- endpoint: "http://example.com/",
5
- endpoints: {},
6
- transports: {},
7
- getClientId: function() {}
8
- }
9
-
10
- if (Faye.Transport.NodeLocal) {
11
- this.LocalTransport = Faye.Transport.NodeLocal
12
- this.HttpTransport = Faye.Transport.NodeHttp
13
- this.inProcess = "in-process"
14
- this.longPolling = "long-polling"
15
- } else {
16
- this.LocalTransport = Faye.Transport.WebSocket
17
- this.HttpTransport = Faye.Transport.XHR
18
- this.inProcess = "websocket"
19
- this.longPolling = "long-polling"
20
- }
21
- }})
22
-
23
- describe("get", function() { with(this) {
24
- before(function() { with(this) {
25
- stub(HttpTransport, "isUsable").yields([false])
26
- stub(LocalTransport, "isUsable").yields([false])
27
- }})
28
-
29
- describe("when no transport is usable", function() { with(this) {
30
- it("raises an exception", function() { with(this) {
31
- assertThrows(Error, function() { Faye.Transport.get(client, [longPolling, inProcess], []) })
32
- }})
33
- }})
34
-
35
- describe("when a less preferred transport is usable", function() { with(this) {
36
- before(function() { with(this) {
37
- stub(HttpTransport, "isUsable").yields([true])
38
- }})
39
-
40
- it("returns a transport of the usable type", function() { with(this) {
41
- Faye.Transport.get(client, [longPolling, inProcess], [], function(transport) {
42
- assertKindOf( HttpTransport, transport )
43
- })
44
- }})
45
-
46
- it("raises an exception if the usable type is not requested", function() { with(this) {
47
- assertThrows(Error, function() { Faye.Transport.get(client, [inProcess], []) })
48
- }})
49
-
50
- it("allows the usable type to be specifically selected", function() { with(this) {
51
- Faye.Transport.get(client, [longPolling], [], function(transport) {
52
- assertKindOf( HttpTransport, transport )
53
- })
54
- }})
55
- }})
56
-
57
- describe("when all transports are usable", function() { with(this) {
58
- before(function() { with(this) {
59
- stub(LocalTransport, "isUsable").yields([true])
60
- stub(HttpTransport, "isUsable").yields([true])
61
- }})
62
-
63
- it("returns the most preferred type", function() { with(this) {
64
- Faye.Transport.get(client, [longPolling, inProcess], [], function(transport) {
65
- assertKindOf( LocalTransport, transport )
66
- })
67
- }})
68
-
69
- it("allows types to be specifically selected", function() { with(this) {
70
- Faye.Transport.get(client, [inProcess], [], function(transport) {
71
- assertKindOf( LocalTransport, transport )
72
- })
73
- Faye.Transport.get(client, [longPolling], [], function(transport) {
74
- assertKindOf( HttpTransport, transport )
75
- })
76
- }})
77
- }})
78
- }})
79
-
80
- describe("send", function() { with(this) {
81
- include(JS.Test.FakeClock)
82
- before(function() { this.clock.stub() })
83
- after(function() { this.clock.reset() })
84
-
85
- describe("for batching transports", function() { with(this) {
86
- before(function() { with(this) {
87
- this.Transport = Faye.Class(Faye.Transport, {batching: true})
88
- this.transport = new Transport(client)
89
- }})
90
-
91
- it("does not make an immediate request", function() { with(this) {
92
- expect(transport, "request").exactly(0)
93
- transport.send({batch: "me"}, 60)
94
- }})
95
-
96
- it("queues the message to be sent after a timeout", function() { with(this) {
97
- expect(transport, "request").given([{batch: "me"}], 60)
98
- transport.send({batch: "me"}, 60)
99
- clock.tick(10)
100
- }})
101
-
102
- it("allows multiple messages to be batched together", function() { with(this) {
103
- expect(transport, "request").given([{id: 1}, {id: 2}], 60)
104
- transport.send({id: 1}, 60)
105
- transport.send({id: 2}, 60)
106
- clock.tick(10)
107
- }})
108
-
109
- it("adds advice to connect messages sent with others", function() { with(this) {
110
- expect(transport, "request").given([{channel: "/meta/connect", advice: {timeout: 0}}, {}], 60)
111
- transport.send({channel: "/meta/connect"}, 60)
112
- transport.send({}, 60)
113
- clock.tick(10)
114
- }})
115
-
116
- it("adds no advice to connect messages sent alone", function() { with(this) {
117
- expect(transport, "request").given([{channel: "/meta/connect"}], 60)
118
- transport.send({channel: "/meta/connect"}, 60)
119
- clock.tick(10)
120
- }})
121
- }})
122
-
123
- describe("for non-batching transports", function() { with(this) {
124
- before(function() { with(this) {
125
- this.Transport = Faye.Class(Faye.Transport, {batching: false})
126
- this.transport = new Transport(client)
127
- }})
128
-
129
- it("makes a request immediately", function() { with(this) {
130
- expect(transport, "request").given([{no: "batch"}], 60)
131
- transport.send({no: "batch"}, 60)
132
- }})
133
- }})
134
- }})
135
- }})
@@ -1,55 +0,0 @@
1
- require('jsclass')
2
- Faye = require('../build/node/faye-node')
3
- Faye.logger = function() {}
4
-
5
- JS.Packages(function() { with(this) {
6
- autoload(/.*Spec/, {from: 'spec/javascript'})
7
- }})
8
-
9
- FakeSocket = function() {
10
- this._fragments = []
11
- }
12
- FakeSocket.prototype.write = function(buffer, encoding) {
13
- this._fragments.push([buffer, encoding])
14
- }
15
- FakeSocket.prototype.read = function() {
16
- var output = []
17
- this._fragments.forEach(function(buffer, i) {
18
- for (var j = 0, n = buffer[0].length; j < n; j++)
19
- output.push(buffer[0][j])
20
- })
21
- return output
22
- }
23
- FakeSocket.prototype.addListener = function() {}
24
-
25
- JS.require('Faye', 'JS.Test', 'JS.Range', function() {
26
- JS.Test.Unit.Assertions.include({
27
- assertYield: function(expected) {
28
- var testcase = this
29
- return function(actual) { testcase.assertEqual(expected, actual) }
30
- }
31
- })
32
-
33
- JS.ENV.Engine = {}
34
- JS.ENV.Server = {}
35
-
36
- JS.require( 'FayeSpec',
37
- 'GrammarSpec',
38
- 'PublisherSpec',
39
- 'ChannelSpec',
40
- 'EngineSpec',
41
- 'Engine.MemorySpec',
42
- 'ServerSpec',
43
- 'Server.HandshakeSpec',
44
- 'Server.ConnectSpec',
45
- 'Server.DisconnectSpec',
46
- 'Server.SubscribeSpec',
47
- 'Server.UnsubscribeSpec',
48
- 'Server.PublishSpec',
49
- 'Server.ExtensionsSpec',
50
- 'Server.IntegrationSpec',
51
- 'NodeAdapterSpec',
52
- 'ClientSpec',
53
- 'TransportSpec',
54
- JS.Test.method('autorun'))
55
- })
@@ -1,17 +0,0 @@
1
- // This script should be run with PhantomJS
2
- // http://www.phantomjs.org/
3
-
4
- var page = new WebPage()
5
-
6
- page.onConsoleMessage = function(message) {
7
- try {
8
- var result = JSON.parse(message)
9
- if ('total' in result && 'fail' in result) {
10
- console.log(message)
11
- var status = (!result.fail && !result.error) ? 0 : 1
12
- phantom.exit(status)
13
- }
14
- } catch (e) {}
15
- }
16
-
17
- page.open('spec/browser.html')
@@ -1,17 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Faye::Channel do
4
- describe :expand do
5
- it "returns all patterns that match a channel" do
6
- Faye::Channel.expand("/foo").should == [
7
- "/**", "/foo", "/*"]
8
-
9
- Faye::Channel.expand("/foo/bar").should == [
10
- "/**", "/foo/bar", "/foo/*", "/foo/**"]
11
-
12
- Faye::Channel.expand("/foo/bar/qux").should == [
13
- "/**", "/foo/bar/qux", "/foo/bar/*", "/foo/**", "/foo/bar/**"]
14
- end
15
- end
16
- end
17
-
@@ -1,741 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Faye::Client do
4
- let :transport do
5
- transport = mock("transport", :cookies= => nil, :endpoint => "http://www.example.com/faye", :headers= => nil)
6
- transport.stub(:connection_type).and_return "fake"
7
- transport.stub(:send)
8
- transport.extend(Faye::Publisher)
9
- transport
10
- end
11
-
12
- before { EM.stub(:add_timer) }
13
-
14
- def stub_response(response)
15
- transport.stub(:send) do |message, *args|
16
- response["id"] = message["id"]
17
- @client.receive_message(response)
18
- end
19
- end
20
-
21
- def create_client
22
- Faye::Transport.stub(:get).and_yield(transport)
23
- @client = Faye::Client.new("http://localhost/")
24
- end
25
-
26
- def create_connected_client
27
- create_client
28
- stub_response "channel" => "/meta/handshake",
29
- "successful" => true,
30
- "version" => "1.0",
31
- "supportedConnectionTypes" => ["websocket"],
32
- "clientId" => "fakeid"
33
-
34
- @client.handshake
35
- end
36
-
37
- def subscribe(client, channel, callback = nil)
38
- stub_response "channel" => "/meta/subscribe",
39
- "successful" => true,
40
- "clientId" => "fakeid",
41
- "subscription" => channel
42
-
43
- @subs_called = 0
44
- callback ||= lambda { |m| @subs_called = 1 }
45
- @client.subscribe(channel, &callback)
46
- end
47
-
48
- describe :initialize do
49
- it "puts the client in the UNCONNECTED state" do
50
- Faye::Transport.stub(:get)
51
- client = Faye::Client.new("http://localhost/")
52
- client.state.should == :UNCONNECTED
53
- end
54
- end
55
-
56
- describe :handshake do
57
- before { create_client }
58
-
59
- it "creates a transport the server must support" do
60
- Faye::Transport.should_receive(:get).with(instance_of(Faye::Client),
61
- ["long-polling", "callback-polling", "in-process"],
62
- []).
63
- and_yield(transport)
64
- @client.handshake
65
- end
66
-
67
- it "sends a handshake message to the server" do
68
- transport.should_receive(:send).with({
69
- "channel" => "/meta/handshake",
70
- "version" => "1.0",
71
- "supportedConnectionTypes" => ["fake"],
72
- "id" => instance_of(String)
73
- }, 60)
74
- @client.handshake
75
- end
76
-
77
- it "puts the client in the CONNECTING state" do
78
- transport.stub(:send)
79
- @client.handshake
80
- @client.state.should == :CONNECTING
81
- end
82
-
83
- describe "with an outgoing extension installed" do
84
- before do
85
- extension = Class.new do
86
- def outgoing(message, callback)
87
- message["ext"] = {"auth" => "password"}
88
- callback.call(message)
89
- end
90
- end
91
- @client.add_extension(extension.new)
92
- end
93
-
94
- it "passes the handshake message through the extension" do
95
- transport.should_receive(:send).with({
96
- "channel" => "/meta/handshake",
97
- "version" => "1.0",
98
- "supportedConnectionTypes" => ["fake"],
99
- "id" => instance_of(String),
100
- "ext" => {"auth" => "password"}
101
- }, 60)
102
- @client.handshake
103
- end
104
- end
105
-
106
- describe "on successful response" do
107
- before do
108
- stub_response "channel" => "/meta/handshake",
109
- "successful" => true,
110
- "version" => "1.0",
111
- "supportedConnectionTypes" => ["long-polling", "websocket"],
112
- "clientId" => "fakeid"
113
- end
114
-
115
- it "stores the clientId" do
116
- @client.handshake
117
- @client.client_id.should == "fakeid"
118
- end
119
-
120
- it "puts the client in the CONNECTED state" do
121
- @client.handshake
122
- @client.state.should == :CONNECTED
123
- end
124
-
125
- it "registers any pre-existing subscriptions" do
126
- @client.should_receive(:subscribe).with([], true)
127
- @client.handshake
128
- end
129
-
130
- it "selects a new transport based on what the server supports" do
131
- Faye::Transport.should_receive(:get).with(instance_of(Faye::Client),
132
- ["long-polling", "websocket"],
133
- []).
134
- and_return(transport)
135
- @client.handshake
136
- end
137
-
138
- describe "with websocket disabled" do
139
- before { @client.disable("websocket") }
140
-
141
- it "selects a new transport, excluding websocket" do
142
- Faye::Transport.should_receive(:get).with(instance_of(Faye::Client),
143
- ["long-polling", "websocket"],
144
- ["websocket"]).
145
- and_return(transport)
146
- @client.handshake
147
- end
148
- end
149
- end
150
-
151
- describe "on unsuccessful response" do
152
- before do
153
- stub_response "channel" => "/meta/handshake",
154
- "successful" => false,
155
- "version" => "1.0",
156
- "supportedConnectionTypes" => ["websocket"]
157
- end
158
-
159
- it "schedules a retry" do
160
- EM.should_receive(:add_timer)
161
- @client.handshake
162
- end
163
-
164
- it "puts the client in the UNCONNECTED state" do
165
- EM.stub(:add_timer)
166
- @client.handshake
167
- @client.state.should == :UNCONNECTED
168
- end
169
- end
170
-
171
- describe "with existing subscriptions after a server restart" do
172
- before do
173
- create_connected_client
174
-
175
- @message = nil
176
- subscribe @client, "/messages/foo", lambda { |m| @message = m }
177
-
178
- @client.receive_message "advice" => {"reconnect" => "handshake"}
179
-
180
- stub_response "channel" => "/meta/handshake",
181
- "successful" => true,
182
- "version" => "1.0",
183
- "supportedConnectionTypes" => ["websocket"],
184
- "clientId" => "reconnectid"
185
- end
186
-
187
- it "resends the subscriptions to the server" do
188
- transport.should_receive(:send).with(hash_including("channel" => "/meta/handshake"), 60)
189
- transport.should_receive(:send).with({
190
- "channel" => "/meta/subscribe",
191
- "clientId" => "reconnectid",
192
- "subscription" => "/messages/foo",
193
- "id" => instance_of(String)
194
- }, 60)
195
- @client.handshake
196
- end
197
-
198
- it "retains the listeners for the subscriptions" do
199
- @client.handshake
200
- @client.receive_message("channel" => "/messages/foo", "data" => "ok")
201
- @message.should == "ok"
202
- end
203
- end
204
-
205
- describe "with a connected client" do
206
- before { create_connected_client }
207
-
208
- it "does not send a handshake message to the server" do
209
- transport.should_not_receive(:send).with({
210
- "channel" => "/meta/handshake",
211
- "version" => "1.0",
212
- "supportedConnectionTypes" => ["fake"],
213
- "id" => instance_of(String)
214
- }, 60)
215
- @client.handshake
216
- end
217
- end
218
- end
219
-
220
- describe :connect do
221
- describe "with an unconnected client" do
222
- before do
223
- stub_response "channel" => "/meta/handshake",
224
- "successful" => true,
225
- "version" => "1.0",
226
- "supportedConnectionTypes" => ["websocket"],
227
- "clientId" => "handshakeid"
228
-
229
- create_client
230
- end
231
-
232
- it "handshakes before connecting" do
233
- transport.should_receive(:send).with({
234
- "channel" => "/meta/connect",
235
- "clientId" => "handshakeid",
236
- "connectionType" => "fake",
237
- "id" => instance_of(String)
238
- }, 60)
239
- @client.connect
240
- end
241
- end
242
-
243
- describe "with a connected client" do
244
- before { create_connected_client }
245
-
246
- it "sends a connect message to the server" do
247
- transport.should_receive(:send).with({
248
- "channel" => "/meta/connect",
249
- "clientId" => "fakeid",
250
- "connectionType" => "fake",
251
- "id" => instance_of(String)
252
- }, 60)
253
- @client.connect
254
- end
255
-
256
- it "only opens one connect request at a time" do
257
- transport.should_receive(:send).with({
258
- "channel" => "/meta/connect",
259
- "clientId" => "fakeid",
260
- "connectionType" => "fake",
261
- "id" => instance_of(String)
262
- }, 60).
263
- exactly(1).
264
- and_return # override stub implementation
265
-
266
- @client.connect
267
- @client.connect
268
- end
269
- end
270
- end
271
-
272
- describe :disconnect do
273
- before { create_connected_client }
274
-
275
- it "sends a disconnect message to the server" do
276
- transport.stub(:close)
277
- transport.should_receive(:send).with({
278
- "channel" => "/meta/disconnect",
279
- "clientId" => "fakeid",
280
- "id" => instance_of(String)
281
- }, 60)
282
- @client.disconnect
283
- end
284
-
285
- it "puts the client in the DISCONNECTED state" do
286
- transport.stub(:close)
287
- @client.disconnect
288
- @client.state.should == :DISCONNECTED
289
- end
290
-
291
- describe "on successful response" do
292
- before do
293
- stub_response "channel" => "/meta/disconnect",
294
- "successful" => true,
295
- "clientId" => "fakeid"
296
- end
297
-
298
- it "closes the transport" do
299
- transport.should_receive(:close)
300
- @client.disconnect
301
- end
302
- end
303
- end
304
-
305
- describe :subscribe do
306
- before do
307
- create_connected_client
308
- @subscribe_message = {
309
- "channel" => "/meta/subscribe",
310
- "clientId" => "fakeid",
311
- "subscription" => "/foo/*",
312
- "id" => instance_of(String)
313
- }
314
- end
315
-
316
- describe "with no prior subscriptions" do
317
- it "sends a subscribe message to the server" do
318
- transport.should_receive(:send).with(@subscribe_message, 60)
319
- @client.subscribe("/foo/*")
320
- end
321
-
322
- # The Bayeux spec says the server should accept a list of subscriptions
323
- # in one message but the cometD server doesn't actually support this
324
- describe "with an array of subscriptions" do
325
- it "sends multiple subscribe messages" do
326
- transport.should_receive(:send).with({
327
- "channel" => "/meta/subscribe",
328
- "clientId" => "fakeid",
329
- "subscription" => "/foo",
330
- "id" => instance_of(String)
331
- }, 60)
332
- transport.should_receive(:send).with({
333
- "channel" => "/meta/subscribe",
334
- "clientId" => "fakeid",
335
- "subscription" => "/bar",
336
- "id" => instance_of(String)
337
- }, 60)
338
- @client.subscribe(["/foo", "/bar"])
339
- end
340
-
341
- it "returns an array of subscriptions" do
342
- transport.stub(:send)
343
- subs = @client.subscribe(["/foo", "/bar"])
344
- subs.size.should == 2
345
- subs.should be_all { |s| Faye::Subscription === s }
346
- end
347
- end
348
-
349
- describe "on successful response" do
350
- before do
351
- stub_response "channel" => "/meta/subscribe",
352
- "successful" => true,
353
- "clientId" => "fakeid",
354
- "subscription" => "/foo/*"
355
- end
356
-
357
- it "sets up a listener for the subscribed channel" do
358
- @message = nil
359
- @client.subscribe("/foo/*") { |m| @message = m }
360
- @client.receive_message("channel" => "/foo/bar", "data" => "hi")
361
- @message.should == "hi"
362
- end
363
-
364
- it "does not call the listener for non-matching channels" do
365
- @message = nil
366
- @client.subscribe("/foo/*") { |m| @message = m }
367
- @client.receive_message("channel" => "/bar", "data" => "hi")
368
- @message.should be_nil
369
- end
370
-
371
- it "activates the subscription" do
372
- active = false
373
- @client.subscribe("/foo/*").callback { active = true }
374
- active.should be_true
375
- end
376
-
377
- describe "with an incoming extension installed" do
378
- before do
379
- extension = Class.new do
380
- def incoming(message, callback)
381
- message["data"]["changed"] = true if message["data"]
382
- callback.call(message)
383
- end
384
- end
385
- @client.add_extension(extension.new)
386
- @message = nil
387
- @client.subscribe("/foo/*") { |m| @message = m }
388
- end
389
-
390
- it "passes delivered messages through the extension" do
391
- @client.receive_message("channel" => "/foo/bar", "data" => {"hello" => "there"})
392
- @message.should == {"hello" => "there", "changed" => true}
393
- end
394
- end
395
-
396
- describe "with an outgoing extension installed" do
397
- before do
398
- extension = Class.new do
399
- def outgoing(message, callback)
400
- message["data"]["changed"] = true if message["data"]
401
- callback.call(message)
402
- end
403
- end
404
- @client.add_extension(extension.new)
405
- @message = nil
406
- @client.subscribe("/foo/*") { |m| @message = m }
407
- end
408
-
409
- it "leaves messages unchanged" do
410
- @client.receive_message("channel" => "/foo/bar", "data" => {"hello" => "there"})
411
- @message.should == {"hello" => "there"}
412
- end
413
- end
414
-
415
- describe "with an incoming extension that invalidates the response" do
416
- before do
417
- extension = Class.new do
418
- def incoming(message, callback)
419
- message["successful"] = false if message["channel"] == "/meta/subscribe"
420
- callback.call(message)
421
- end
422
- end
423
- @client.add_extension(extension.new)
424
- end
425
-
426
- it "does not set up a listener for the subscribed channel" do
427
- @message = nil
428
- @client.subscribe("/foo/*") { |m| @message = m }
429
- @client.receive_message("channel" => "/foo/bar", "data" => "hi")
430
- @message.should be_nil
431
- end
432
-
433
- it "does not activate the subscription" do
434
- active = false
435
- @client.subscribe("/foo/*").callback { active = true }
436
- active.should be_false
437
- end
438
- end
439
- end
440
-
441
- describe "on unsuccessful response" do
442
- before do
443
- stub_response "channel" => "/meta/subscribe",
444
- "error" => "403:/meta/foo:Forbidden channel",
445
- "successful" => false,
446
- "clientId" => "fakeid",
447
- "subscription" => "/meta/foo"
448
- end
449
-
450
- it "does not set up a listener for the subscribed channel" do
451
- @message = nil
452
- @client.subscribe("/meta/foo") { |m| @message = m }
453
- @client.receive_message("channel" => "/meta/foo", "data" => "hi")
454
- @message.should be_nil
455
- end
456
-
457
- it "does not activate the subscription" do
458
- active = false
459
- @client.subscribe("/meta/foo").callback { active = true }
460
- active.should be_false
461
- end
462
-
463
- it "reports the error through an errback" do
464
- error = nil
465
- @client.subscribe("/meta/foo").errback { |e| error = e }
466
- error.code.should == 403
467
- error.params.should == ["/meta/foo"]
468
- error.message.should == "Forbidden channel"
469
- end
470
- end
471
- end
472
-
473
- describe "with an existing subscription" do
474
- before do
475
- subscribe @client, "/foo/*"
476
- end
477
-
478
- it "does not send another subscribe message to the server" do
479
- transport.should_not_receive(:send).with(@subscribe_message, 60)
480
- @client.subscribe("/foo/*")
481
- end
482
-
483
- it "sets up another listener on the channel" do
484
- @client.subscribe("/foo/*") { @subs_called += 1 }
485
- @client.receive_message("channel" => "/foo/bar", "data" => "hi")
486
- @subs_called.should == 2
487
- end
488
-
489
- it "activates the subscription" do
490
- active = false
491
- @client.subscribe("/foo/*").callback { active = true }
492
- active.should be_true
493
- end
494
- end
495
- end
496
-
497
- describe :unsubscribe do
498
- before do
499
- create_connected_client
500
- @unsubscribe_message = {
501
- "channel" => "/meta/unsubscribe",
502
- "clientId" => "fakeid",
503
- "subscription" => "/foo/*",
504
- "id" => instance_of(String)
505
- }
506
- end
507
-
508
- describe "with no subscriptions" do
509
- it "does not send an unsubscribe message to the server" do
510
- transport.should_not_receive(:send).with(@unsubscribe_message, 60)
511
- @client.unsubscribe("/foo/*")
512
- end
513
- end
514
-
515
- describe "with a single subscription" do
516
- before do
517
- @message = nil
518
- @listener = lambda { |m| @message = m }
519
- subscribe @client, "/foo/*", @listener
520
- end
521
-
522
- it "sends an unsubscribe message to the server" do
523
- transport.should_receive(:send).with(@unsubscribe_message, 60)
524
- @client.unsubscribe("/foo/*")
525
- end
526
-
527
- it "removes the listener from the channel" do
528
- @client.receive_message("channel" => "/foo/bar", "data" => "first")
529
- @client.unsubscribe("/foo/*", &@listener)
530
- @client.receive_message("channel" => "/foo/bar", "data" => "second")
531
- @message.should == "first"
532
- end
533
- end
534
-
535
- describe "with multiple subscriptions to the same channel" do
536
- before do
537
- @messages = []
538
- @hey = lambda { |m| @messages << ("hey " + m["text"]) }
539
- @bye = lambda { |m| @messages << ("bye " + m["text"]) }
540
- subscribe @client, "/foo/*", @hey
541
- subscribe @client, "/foo/*", @bye
542
- end
543
-
544
- it "removes one of the listeners from the channel" do
545
- @client.receive_message("channel" => "/foo/bar", "data" => {"text" => "you"})
546
- @client.unsubscribe("/foo/*", &@hey)
547
- @client.receive_message("channel" => "/foo/bar", "data" => {"text" => "you"})
548
- @messages.should == ["hey you", "bye you", "bye you"]
549
- end
550
-
551
- it "does not send an unsubscribe message if one listener is removed" do
552
- transport.should_not_receive(:send).with(@unsubscribe_message, 60)
553
- @client.unsubscribe("/foo/*", &@bye)
554
- end
555
-
556
- it "sends an unsubscribe message if each listener is removed" do
557
- transport.should_receive(:send).with(@unsubscribe_message, 60)
558
- @client.unsubscribe("/foo/*", &@bye)
559
- @client.unsubscribe("/foo/*", &@hey)
560
- end
561
-
562
- it "sends an unsubscribe message if all listeners are removed" do
563
- transport.should_receive(:send).with(@unsubscribe_message, 60)
564
- @client.unsubscribe("/foo/*")
565
- end
566
- end
567
-
568
- describe "with multiple subscriptions to different channels" do
569
- before do
570
- subscribe @client, "/foo"
571
- subscribe @client, "/bar"
572
- end
573
-
574
- it "sends multiple unsubscribe messages if given an array" do
575
- transport.should_receive(:send).with({
576
- "channel" => "/meta/unsubscribe",
577
- "clientId" => "fakeid",
578
- "subscription" => "/foo",
579
- "id" => instance_of(String)
580
- }, 60)
581
- transport.should_receive(:send).with({
582
- "channel" => "/meta/unsubscribe",
583
- "clientId" => "fakeid",
584
- "subscription" => "/bar",
585
- "id" => instance_of(String)
586
- }, 60)
587
- @client.unsubscribe(["/foo", "/bar"])
588
- end
589
- end
590
- end
591
-
592
- describe :publish do
593
- before { create_connected_client }
594
-
595
- it "sends the message to the server with an ID" do
596
- transport.should_receive(:send).with({
597
- "channel" => "/messages/foo",
598
- "clientId" => "fakeid",
599
- "data" => {"hello" => "world"},
600
- "id" => instance_of(String)
601
- }, 60)
602
- @client.publish("/messages/foo", "hello" => "world")
603
- end
604
-
605
- it "throws an error when publishing to an invalid channel" do
606
- transport.should_not_receive(:send).with(hash_including("channel" => "/messages/*"), 60)
607
- lambda { @client.publish("/messages/*", "hello" => "world") }.should raise_error
608
- end
609
-
610
- describe "on publish failure" do
611
- before do
612
- stub_response "channel" => "/messages/foo",
613
- "error" => "407:/messages/foo:Failed to publish",
614
- "successful" => false,
615
- "clientId" => "fakeid"
616
- end
617
-
618
- it "should not be published" do
619
- published = false
620
- @client.publish("/messages/foo", "text" => "hi").callback { published = true }
621
- published.should be_false
622
- end
623
-
624
- it "reports the error through an errback" do
625
- error = nil
626
- @client.publish("/messages/foo", "text" => "hi").errback { |e| error = e }
627
- error.code.should == 407
628
- error.params.should == ["/messages/foo"]
629
- error.message.should == "Failed to publish"
630
- end
631
- end
632
-
633
- describe "on receipt of the published message" do
634
- before do
635
- stub_response "channel" => "/messages/foo",
636
- "data" => {"text" => "hi"},
637
- "clientId" => "fakeid"
638
- end
639
-
640
- it "does not trigger the callbacks" do
641
- published = false
642
- publication = @client.publish("/messages/foo", "text" => "hi")
643
- publication.callback { published = true }
644
- publication.errback { published = true }
645
- published.should be_false
646
- end
647
- end
648
-
649
- describe "with an outgoing extension installed" do
650
- before do
651
- extension = Class.new do
652
- def outgoing(message, callback)
653
- message["ext"] = {"auth" => "password"}
654
- callback.call(message)
655
- end
656
- end
657
- @client.add_extension(extension.new)
658
- end
659
-
660
- it "passes messages through the extension" do
661
- transport.should_receive(:send).with({
662
- "channel" => "/messages/foo",
663
- "clientId" => "fakeid",
664
- "data" => {"hello" => "world"},
665
- "id" => instance_of(String),
666
- "ext" => {"auth" => "password"}
667
- }, 60)
668
- @client.publish("/messages/foo", "hello" => "world")
669
- end
670
- end
671
-
672
- describe "with an incoming extension installed" do
673
- before do
674
- extension = Class.new do
675
- def incoming(message, callback)
676
- message["ext"] = {"auth" => "password"}
677
- callback.call(message)
678
- end
679
- end
680
- @client.add_extension(extension.new)
681
- end
682
-
683
- it "leaves the message unchanged" do
684
- transport.should_receive(:send).with({
685
- "channel" => "/messages/foo",
686
- "clientId" => "fakeid",
687
- "data" => {"hello" => "world"},
688
- "id" => instance_of(String)
689
- }, 60)
690
- @client.publish("/messages/foo", "hello" => "world")
691
- end
692
- end
693
- end
694
-
695
- describe "network notifications" do
696
- before {
697
- create_client
698
- @client.handshake
699
- }
700
-
701
- describe "in the default state" do
702
- it "broadcasts a down notification" do
703
- @client.should_receive(:trigger).with("transport:down")
704
- transport.trigger(:down)
705
- end
706
-
707
- it "broadcasts an up notification" do
708
- @client.should_receive(:trigger).with("transport:up")
709
- transport.trigger(:up)
710
- end
711
- end
712
-
713
- describe "when the transport is up" do
714
- before { transport.trigger(:up) }
715
-
716
- it "broadcasts a down notification" do
717
- @client.should_receive(:trigger).with("transport:down")
718
- transport.trigger(:down)
719
- end
720
-
721
- it "does not broadcast an up notification" do
722
- @client.should_not_receive(:trigger)
723
- transport.trigger(:up)
724
- end
725
- end
726
-
727
- describe "when the transport is down" do
728
- before { transport.trigger(:down) }
729
-
730
- it "does not broadcast a down notification" do
731
- @client.should_not_receive(:trigger)
732
- transport.trigger(:down)
733
- end
734
-
735
- it "broadcasts an up notification" do
736
- @client.should_receive(:trigger).with("transport:up")
737
- transport.trigger(:up)
738
- end
739
- end
740
- end
741
- end