face-faye 0.8.9

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.
Files changed (73) hide show
  1. data/History.txt +304 -0
  2. data/README.rdoc +83 -0
  3. data/lib/faye-browser-min.js +2 -0
  4. data/lib/faye-browser-min.js.map +8 -0
  5. data/lib/faye-browser.js +2194 -0
  6. data/lib/faye.rb +122 -0
  7. data/lib/faye/adapters/rack_adapter.rb +216 -0
  8. data/lib/faye/adapters/static_server.rb +56 -0
  9. data/lib/faye/engines/connection.rb +60 -0
  10. data/lib/faye/engines/memory.rb +112 -0
  11. data/lib/faye/engines/proxy.rb +121 -0
  12. data/lib/faye/error.rb +49 -0
  13. data/lib/faye/mixins/logging.rb +47 -0
  14. data/lib/faye/mixins/publisher.rb +30 -0
  15. data/lib/faye/mixins/timeouts.rb +22 -0
  16. data/lib/faye/protocol/channel.rb +124 -0
  17. data/lib/faye/protocol/client.rb +376 -0
  18. data/lib/faye/protocol/extensible.rb +43 -0
  19. data/lib/faye/protocol/grammar.rb +58 -0
  20. data/lib/faye/protocol/publication.rb +5 -0
  21. data/lib/faye/protocol/server.rb +293 -0
  22. data/lib/faye/protocol/socket.rb +23 -0
  23. data/lib/faye/protocol/subscription.rb +24 -0
  24. data/lib/faye/transport/http.rb +76 -0
  25. data/lib/faye/transport/local.rb +22 -0
  26. data/lib/faye/transport/transport.rb +116 -0
  27. data/lib/faye/transport/web_socket.rb +92 -0
  28. data/lib/faye/util/namespace.rb +20 -0
  29. data/spec/browser.html +45 -0
  30. data/spec/encoding_helper.rb +7 -0
  31. data/spec/install.sh +78 -0
  32. data/spec/javascript/channel_spec.js +15 -0
  33. data/spec/javascript/client_spec.js +729 -0
  34. data/spec/javascript/engine/memory_spec.js +7 -0
  35. data/spec/javascript/engine_spec.js +417 -0
  36. data/spec/javascript/faye_spec.js +34 -0
  37. data/spec/javascript/grammar_spec.js +66 -0
  38. data/spec/javascript/node_adapter_spec.js +307 -0
  39. data/spec/javascript/publisher_spec.js +27 -0
  40. data/spec/javascript/server/connect_spec.js +168 -0
  41. data/spec/javascript/server/disconnect_spec.js +121 -0
  42. data/spec/javascript/server/extensions_spec.js +60 -0
  43. data/spec/javascript/server/handshake_spec.js +145 -0
  44. data/spec/javascript/server/integration_spec.js +131 -0
  45. data/spec/javascript/server/publish_spec.js +85 -0
  46. data/spec/javascript/server/subscribe_spec.js +247 -0
  47. data/spec/javascript/server/unsubscribe_spec.js +245 -0
  48. data/spec/javascript/server_spec.js +121 -0
  49. data/spec/javascript/transport_spec.js +135 -0
  50. data/spec/node.js +55 -0
  51. data/spec/phantom.js +17 -0
  52. data/spec/ruby/channel_spec.rb +17 -0
  53. data/spec/ruby/client_spec.rb +741 -0
  54. data/spec/ruby/engine/memory_spec.rb +7 -0
  55. data/spec/ruby/engine_examples.rb +427 -0
  56. data/spec/ruby/faye_spec.rb +30 -0
  57. data/spec/ruby/grammar_spec.rb +68 -0
  58. data/spec/ruby/publisher_spec.rb +27 -0
  59. data/spec/ruby/rack_adapter_spec.rb +236 -0
  60. data/spec/ruby/server/connect_spec.rb +170 -0
  61. data/spec/ruby/server/disconnect_spec.rb +120 -0
  62. data/spec/ruby/server/extensions_spec.rb +68 -0
  63. data/spec/ruby/server/handshake_spec.rb +143 -0
  64. data/spec/ruby/server/integration_spec.rb +133 -0
  65. data/spec/ruby/server/publish_spec.rb +81 -0
  66. data/spec/ruby/server/subscribe_spec.rb +247 -0
  67. data/spec/ruby/server/unsubscribe_spec.rb +247 -0
  68. data/spec/ruby/server_spec.rb +121 -0
  69. data/spec/ruby/transport_spec.rb +136 -0
  70. data/spec/spec_helper.rb +11 -0
  71. data/spec/testswarm +42 -0
  72. data/spec/thin_proxy.rb +37 -0
  73. metadata +441 -0
@@ -0,0 +1,7 @@
1
+ JS.ENV.Engine.MemorySpec = JS.Test.describe("Memory engine", function() { with(this) {
2
+ before(function() {
3
+ this.engineOpts = {type: Faye.Engine.Memory}
4
+ })
5
+
6
+ itShouldBehaveLike("faye engine")
7
+ }})
@@ -0,0 +1,417 @@
1
+ JS.ENV.EngineSteps = JS.Test.asyncSteps({
2
+ create_client: function(name, resume) {
3
+ var inboxes = this._inboxes = this._inboxes || {}
4
+ var clients = this._clients = this._clients || {}
5
+ this.engine.createClient(function(clientId) {
6
+ clients[name] = clientId
7
+ inboxes[name] = inboxes[name] || []
8
+ resume()
9
+ })
10
+ },
11
+
12
+ connect: function(name, engine, resume) {
13
+ var clientId = this._clients[name]
14
+ var inboxes = this._inboxes
15
+ engine.connect(clientId, {}, function(messages) {
16
+ for (var i = 0, n = messages.length; i < n; i++) {
17
+ delete messages[i].id
18
+ inboxes[name].push(messages[i])
19
+ }
20
+ })
21
+ setTimeout(resume, 10)
22
+ },
23
+
24
+ destroy_client: function(name, resume) {
25
+ this.engine.destroyClient(this._clients[name], resume)
26
+ },
27
+
28
+ check_client_id: function(name, pattern, resume) {
29
+ this.assertMatch(pattern, this._clients[name])
30
+ resume()
31
+ },
32
+
33
+ check_num_clients: function(n, resume) {
34
+ var ids = new JS.Set()
35
+ for (var key in this._clients) ids.add(this._clients[key])
36
+ this.assertEqual(n, ids.count())
37
+ resume()
38
+ },
39
+
40
+ check_client_exists: function(name, exists, resume) {
41
+ var tc = this
42
+ tc.engine.clientExists(tc._clients[name], function(actual) {
43
+ tc.assertEqual(exists, actual)
44
+ resume()
45
+ })
46
+ },
47
+
48
+ subscribe: function(name, channel, resume) {
49
+ this.engine.subscribe(this._clients[name], channel, resume)
50
+ },
51
+
52
+ unsubscribe: function(name, channel, resume) {
53
+ this.engine.unsubscribe(this._clients[name], channel, resume)
54
+ },
55
+
56
+ publish: function(messages, resume) {
57
+ messages = [].concat(messages)
58
+ for (var i = 0, n = messages.length; i < n; i++) {
59
+ var message = Faye.extend({id: Faye.random()}, messages[i])
60
+ this.engine.publish(message)
61
+ }
62
+ setTimeout(resume, 20)
63
+ },
64
+
65
+ publish_by: function(name, message, resume) {
66
+ message = Faye.extend({clientId: this._clients[name], id: Faye.random()}, message)
67
+ this.engine.publish(message)
68
+ setTimeout(resume, 10)
69
+ },
70
+
71
+ ping: function(name, resume) {
72
+ this.engine.ping(this._clients[name])
73
+ resume()
74
+ },
75
+
76
+ clock_tick: function(time, resume) {
77
+ setTimeout(resume, time)
78
+ },
79
+
80
+ expect_event: function(name, event, args, resume) {
81
+ var params = [this._clients[name]].concat(args),
82
+ handler = function() {}
83
+
84
+ this.engine.bind(event, handler)
85
+ this.expect(handler, "apply").given(undefined, params)
86
+ resume()
87
+ },
88
+
89
+ expect_no_event: function(name, event, args, resume) {
90
+ var params = [this._clients[name]].concat(args),
91
+ handler = function() {}
92
+
93
+ this.engine.bind(event, handler)
94
+ this.expect(handler, "apply").exactly(0)
95
+ resume()
96
+ },
97
+
98
+ expect_message: function(name, messages, resume) {
99
+ this.assertEqual(messages, this._inboxes[name])
100
+ resume()
101
+ },
102
+
103
+ expect_no_message: function(name, resume) {
104
+ this.assertEqual([], this._inboxes[name])
105
+ resume()
106
+ },
107
+
108
+ check_different_messages: function(a, b, resume) {
109
+ this.assertNotSame(this._inboxes[a][0], this._inboxes[b][0])
110
+ resume()
111
+ }
112
+ })
113
+
114
+ JS.ENV.EngineSpec = JS.Test.describe("Pub/sub engines", function() { with(this) {
115
+ sharedExamplesFor("faye engine", function() { with(this) {
116
+ include(JS.Test.Helpers)
117
+ include(EngineSteps)
118
+
119
+ define("create_engine", function() { with(this) {
120
+ var opts = Faye.extend(options(), engineOpts)
121
+ return new Faye.Engine.Proxy(opts)
122
+ }})
123
+
124
+ define("options", function() { return {timeout: 1} })
125
+
126
+ before(function() { with(this) {
127
+ this.engine = create_engine()
128
+ create_client("alice")
129
+ create_client("bob")
130
+ create_client("carol")
131
+ }})
132
+
133
+ describe("createClient", function() { with(this) {
134
+ it("returns a client id", function() { with(this) {
135
+ create_client("dave")
136
+ check_client_id("dave", /^[a-z0-9]+$/)
137
+ }})
138
+
139
+ it("returns a different id every time", function() { with(this) {
140
+ $R(1,7).forEach(function(i) { create_client("client" + i) })
141
+ check_num_clients(10)
142
+ }})
143
+
144
+ it("publishes an event", function() { with(this) {
145
+ expect(engine, "trigger").given("handshake", match(/^[a-z0-9]+$/))
146
+ create_client("dave")
147
+ }})
148
+ }})
149
+
150
+ describe("clientExists", function() { with(this) {
151
+ it("returns true if the client id exists", function() { with(this) {
152
+ check_client_exists("alice", true)
153
+ }})
154
+
155
+ it("returns false if the client id does not exist", function() { with(this) {
156
+ check_client_exists("anything", false)
157
+ }})
158
+ }})
159
+
160
+ describe("ping", function() { with(this) {
161
+ define("options", function() { return {timeout: 0.3, gc: 0.08} })
162
+
163
+ it("removes a client if it does not ping often enough", function() { with(this) {
164
+ clock_tick(700)
165
+ check_client_exists("alice", false)
166
+ }})
167
+
168
+ it("prolongs the life of a client", function() { with(this) {
169
+ clock_tick(450)
170
+ ping("alice")
171
+ clock_tick(450)
172
+ check_client_exists("alice", true)
173
+ clock_tick(450)
174
+ check_client_exists("alice", false)
175
+ }})
176
+ }})
177
+
178
+ describe("destroyClient", function() { with(this) {
179
+ it("removes the given client", function() { with(this) {
180
+ destroy_client("alice")
181
+ check_client_exists("alice", false)
182
+ }})
183
+
184
+ it("publishes an event", function() { with(this) {
185
+ expect_event("alice", "disconnect", [])
186
+ destroy_client("alice")
187
+ }})
188
+
189
+ describe("when the client has subscriptions", function() { with(this) {
190
+ before(function() { with(this) {
191
+ this.message = {"channel": "/messages/foo", "data": "ok"}
192
+ subscribe("alice", "/messages/foo")
193
+ }})
194
+
195
+ it("stops the client receiving messages", function() { with(this) {
196
+ connect("alice", engine)
197
+ destroy_client("alice")
198
+ publish(message)
199
+ expect_no_message("alice")
200
+ }})
201
+
202
+ it("publishes an event", function() { with(this) {
203
+ expect_event("alice", "disconnect", [])
204
+ destroy_client("alice")
205
+ }})
206
+ }})
207
+ }})
208
+
209
+ describe("subscribe", function() { with(this) {
210
+ it("publishes an event", function() { with(this) {
211
+ expect_event("alice", "subscribe", ["/messages/foo"])
212
+ subscribe("alice", "/messages/foo")
213
+ }})
214
+
215
+ describe("when the client is subscribed to the channel", function() { with(this) {
216
+ before(function() { this.subscribe("alice", "/messages/foo") })
217
+
218
+ it("does not publish an event", function() { with(this) {
219
+ expect_no_event("alice", "subscribe", ["/messages/foo"])
220
+ subscribe("alice", "/messages/foo")
221
+ }})
222
+ }})
223
+ }})
224
+
225
+
226
+ describe("unsubscribe", function() { with(this) {
227
+ before(function() { this.subscribe("alice", "/messages/bar") })
228
+
229
+ it("does not publish an event", function() { with(this) {
230
+ expect_no_event("alice", "unsubscribe", ["/messages/foo"])
231
+ unsubscribe("alice", "/messages/foo")
232
+ }})
233
+
234
+ describe("when the client is subscribed to the channel", function() { with(this) {
235
+ before(function() { this.subscribe("alice", "/messages/foo") })
236
+
237
+ it("publishes an event", function() { with(this) {
238
+ expect_event("alice", "unsubscribe", ["/messages/foo"])
239
+ unsubscribe("alice", "/messages/foo")
240
+ }})
241
+ }})
242
+ }})
243
+
244
+ describe("publish", function() { with(this) {
245
+ before(function() { with(this) {
246
+ this.message = {"channel": "/messages/foo", "data": "ok", "blank": null}
247
+ connect("alice", engine)
248
+ connect("bob", engine)
249
+ connect("carol", engine)
250
+ }})
251
+
252
+ describe("with no subscriptions", function() { with(this) {
253
+ it("delivers no messages", function() { with(this) {
254
+ publish(message)
255
+ expect_no_message("alice")
256
+ expect_no_message("bob")
257
+ expect_no_message("carol")
258
+ }})
259
+
260
+ it("publishes a :publish event with a clientId", function() { with(this) {
261
+ expect_event("bob", "publish", ["/messages/foo", "ok"])
262
+ publish_by("bob", message)
263
+ }})
264
+
265
+ it("publishes a :publish event with no clientId", function() { with(this) {
266
+ expect_event(null, "publish", ["/messages/foo", "ok"])
267
+ publish(message)
268
+ }})
269
+ }})
270
+
271
+ describe("with a subscriber", function() { with(this) {
272
+ before(function() { with(this) {
273
+ subscribe("alice", "/messages/foo")
274
+ }})
275
+
276
+ it("delivers multibyte messages correctly", function() { with(this) {
277
+ message.data = "Apple = "
278
+ publish(message)
279
+ expect_message("alice", [message])
280
+ }})
281
+
282
+ it("delivers messages to the subscribed client", function() { with(this) {
283
+ publish(message)
284
+ expect_message("alice", [message])
285
+ }})
286
+
287
+ it("publishes a :publish event with a clientId", function() { with(this) {
288
+ expect_event("bob", "publish", ["/messages/foo", "ok"])
289
+ publish_by("bob", message)
290
+ }})
291
+ }})
292
+
293
+ describe("with a subscriber that is removed", function() { with(this) {
294
+ before(function() { with(this) {
295
+ subscribe("alice", "/messages/foo")
296
+ unsubscribe("alice", "/messages/foo")
297
+ }})
298
+
299
+ it("does not deliver messages to unsubscribed clients", function() { with(this) {
300
+ publish(message)
301
+ expect_no_message("alice")
302
+ expect_no_message("bob")
303
+ expect_no_message("carol")
304
+ }})
305
+
306
+ it("publishes a :publish event with a clientId", function() { with(this) {
307
+ expect_event("bob", "publish", ["/messages/foo", "ok"])
308
+ publish_by("bob", message)
309
+ }})
310
+ }})
311
+
312
+ describe("with multiple subscribers", function() { with(this) {
313
+ before(function() { with(this) {
314
+ subscribe("alice", "/messages/foo")
315
+ subscribe("bob", "/messages/bar")
316
+ subscribe("carol", "/messages/foo")
317
+ }})
318
+
319
+ it("delivers messages to the subscribed clients", function() { with(this) {
320
+ publish(message)
321
+ expect_message("alice", [message])
322
+ expect_no_message("bob")
323
+ expect_message("carol", [message])
324
+ }})
325
+ }})
326
+
327
+ describe("with a single wildcard", function() { with(this) {
328
+ before(function() { with(this) {
329
+ subscribe("alice", "/messages/*")
330
+ subscribe("bob", "/messages/bar")
331
+ subscribe("carol", "/*")
332
+ }})
333
+
334
+ it("delivers messages to matching subscriptions", function() { with(this) {
335
+ publish(message)
336
+ expect_message("alice", [message])
337
+ expect_no_message("bob")
338
+ expect_no_message("carol")
339
+ }})
340
+ }})
341
+
342
+ describe("with a double wildcard", function() { with(this) {
343
+ before(function() { with(this) {
344
+ subscribe("alice", "/messages/**")
345
+ subscribe("bob", "/messages/bar")
346
+ subscribe("carol", "/**")
347
+ }})
348
+
349
+ it("delivers messages to matching subscriptions", function() { with(this) {
350
+ publish(message)
351
+ expect_message("alice", [message])
352
+ expect_no_message("bob")
353
+ expect_message("carol", [message])
354
+ }})
355
+
356
+ it("delivers a unique copy of the message to each client", function() { with(this) {
357
+ publish(message)
358
+ check_different_messages("alice", "carol")
359
+ }})
360
+ }})
361
+
362
+ describe("with multiple matching subscriptions for the same client", function() { with(this) {
363
+ before(function() { with(this) {
364
+ subscribe("alice", "/messages/*")
365
+ subscribe("alice", "/messages/foo")
366
+ }})
367
+
368
+ it("delivers each message once to each client", function() { with(this) {
369
+ publish(message)
370
+ expect_message("alice", [message])
371
+ }})
372
+
373
+ it("delivers the message as many times as it is published", function() { with(this) {
374
+ publish([message, message])
375
+ expect_message("alice", [message, message])
376
+ }})
377
+ }})
378
+ }})
379
+ }})
380
+
381
+ sharedBehavior("distributed engine", function() { with(this) {
382
+ include(JS.Test.Helpers)
383
+ include(EngineSteps)
384
+
385
+ define("create_engine", function() { with(this) {
386
+ var opts = Faye.extend(options(), engineOpts)
387
+ return new Faye.Engine.Proxy(opts)
388
+ }})
389
+
390
+ define("options", function() { return {timeout: 1} })
391
+
392
+ before(function() { with(this) {
393
+ this.left = create_engine()
394
+ this.right = create_engine()
395
+ this.engine = left
396
+
397
+ create_client("alice")
398
+ create_client("bob")
399
+
400
+ connect("alice", left)
401
+ }})
402
+
403
+ describe("publish", function() { with(this) {
404
+ before(function() { with(this) {
405
+ subscribe("alice", "/foo")
406
+ publish({channel: "/foo", data: "first"})
407
+ }})
408
+
409
+ it("only delivers each message once", function() { with(this) {
410
+ expect_message("alice", [{channel: "/foo", data: "first"}])
411
+ publish({channel: "/foo", data: "second"})
412
+ connect("alice", right)
413
+ expect_message("alice", [{channel: "/foo", data: "first"}, {channel: "/foo", data: "second"}])
414
+ }})
415
+ }})
416
+ }})
417
+ }})
@@ -0,0 +1,34 @@
1
+ JS.ENV.FayeSpec = JS.Test.describe("Faye", function() { with(this) {
2
+ include(JS.Test.Helpers)
3
+
4
+ describe("random", function() { with(this) {
5
+ it("returns a 128-bit random number in base 36", function() { with(this) {
6
+ assertMatch( /^[a-z0-9]+$/, Faye.random() )
7
+ }})
8
+
9
+ it("always produces the same length of string", function() { with(this) {
10
+ var ids = $R(1,100).map(function() { return Faye.random().length })
11
+ var expected = $R(1,100).map(function() { return 35 })
12
+ assertEqual( expected, ids )
13
+ }})
14
+ }})
15
+
16
+ describe("copyObject", function() { with(this) {
17
+ before(function() { with(this) {
18
+ this.object = {foo: "bar", qux: 42, hey: null, obj: {bar: 67}}
19
+ }})
20
+
21
+ it("returns an equal object", function() { with(this) {
22
+ assertEqual( {foo: "bar", qux: 42, hey: null, obj: {bar: 67}},
23
+ Faye.copyObject(object) )
24
+ }})
25
+
26
+ it("does not return the same object", function() { with(this) {
27
+ assertNotSame( object, Faye.copyObject(object) )
28
+ }})
29
+
30
+ it("performs a deep clone", function() { with(this) {
31
+ assertNotSame( object.obj, Faye.copyObject(object).obj )
32
+ }})
33
+ }})
34
+ }})