faye 0.3.1 → 0.3.2
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.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +12 -0
- data/Manifest.txt +5 -0
- data/README.txt +25 -7
- data/Rakefile +1 -0
- data/build/faye-client-min.js +1 -1
- data/build/faye.js +367 -186
- data/examples/node/faye-client-min.js +1 -1
- data/examples/node/faye.js +367 -186
- data/examples/shared/public/index.html +1 -0
- data/examples/shared/public/mootools.js +4329 -0
- data/examples/shared/public/prototype.js +4874 -0
- data/jake.yml +3 -1
- data/lib/faye-client-min.js +1 -1
- data/lib/faye.rb +5 -2
- data/lib/faye/channel.rb +4 -0
- data/lib/faye/client.rb +53 -29
- data/lib/faye/connection.rb +16 -37
- data/lib/faye/rack_adapter.rb +33 -28
- data/lib/faye/server.rb +24 -23
- data/lib/faye/timeouts.rb +21 -0
- data/lib/faye/transport.rb +17 -4
- data/test/scenario.js +73 -37
- data/test/scenario.rb +104 -0
- data/test/test_channel.rb +7 -1
- data/test/test_clients.js +28 -10
- data/test/test_clients.rb +154 -0
- data/test/test_grammar.rb +1 -1
- data/test/test_server.rb +8 -7
- metadata +17 -11
@@ -0,0 +1,21 @@
|
|
1
|
+
module Faye
|
2
|
+
module Timeouts
|
3
|
+
def add_timeout(name, delay, &block)
|
4
|
+
@timeouts ||= {}
|
5
|
+
return if @timeouts.has_key?(name)
|
6
|
+
@timeouts[name] = EventMachine.add_timer(delay) do
|
7
|
+
@timeouts.delete(name)
|
8
|
+
block.call
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove_timeout(name)
|
13
|
+
@timeouts ||= {}
|
14
|
+
timeout = @timeouts[name]
|
15
|
+
return if timeout.nil?
|
16
|
+
EventMachine.cancel_timer(timeout)
|
17
|
+
@timeouts.delete(name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
data/lib/faye/transport.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'em-http'
|
2
2
|
require 'json'
|
3
|
+
require 'uri'
|
3
4
|
|
4
5
|
module Faye
|
5
6
|
|
@@ -20,10 +21,11 @@ module Faye
|
|
20
21
|
|
21
22
|
request(message) { |responses|
|
22
23
|
if block_given?
|
24
|
+
messages, deliverable = [], true
|
23
25
|
[responses].flatten.each do |response|
|
24
26
|
|
25
27
|
if message.is_a?(Hash) and response['id'] == message['id']
|
26
|
-
block.call(response)
|
28
|
+
deliverable = false if block.call(response) == false
|
27
29
|
end
|
28
30
|
|
29
31
|
if response['advice']
|
@@ -31,10 +33,12 @@ module Faye
|
|
31
33
|
end
|
32
34
|
|
33
35
|
if response['data'] and response['channel']
|
34
|
-
|
36
|
+
messages << response
|
35
37
|
end
|
36
38
|
|
37
39
|
end
|
40
|
+
|
41
|
+
@client.deliver_messages(messages) if deliverable
|
38
42
|
end
|
39
43
|
}
|
40
44
|
end
|
@@ -77,8 +81,17 @@ module Faye
|
|
77
81
|
end
|
78
82
|
|
79
83
|
def request(message, &block)
|
80
|
-
|
81
|
-
|
84
|
+
content = JSON.unparse(message)
|
85
|
+
params = {
|
86
|
+
:head => {
|
87
|
+
'Content-Type' => 'application/json',
|
88
|
+
'host' => URI.parse(@endpoint).host,
|
89
|
+
'Content-Length' => content.length
|
90
|
+
},
|
91
|
+
:body => content,
|
92
|
+
:timeout => -1
|
93
|
+
}
|
94
|
+
request = EventMachine::HttpRequest.new(@endpoint).post(params)
|
82
95
|
request.callback do
|
83
96
|
block.call(JSON.parse(request.response))
|
84
97
|
end
|
data/test/scenario.js
CHANGED
@@ -4,42 +4,46 @@ var sys = require('sys'),
|
|
4
4
|
assert = require('assert'),
|
5
5
|
faye = require('../build/faye');
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
Faye.Logging.logLevel = 'info';
|
8
|
+
|
9
|
+
AsyncScenario = Faye.Class({
|
10
|
+
initialize: function(name) {
|
9
11
|
this._name = name;
|
10
|
-
this._block = block;
|
11
12
|
this._clients = {};
|
12
13
|
this._inbox = {};
|
13
14
|
this._pool = 0;
|
14
15
|
},
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
sys.puts('----------------------------------------------------------------');
|
19
|
-
this._block.call(this);
|
17
|
+
wait: function(time, Continue) {
|
18
|
+
setTimeout(Continue, 1000 * time);
|
20
19
|
},
|
21
20
|
|
22
|
-
server: function(port) {
|
23
|
-
sys.puts('Starting server on port ' + port);
|
21
|
+
server: function(port, Continue) {
|
24
22
|
this._endpoint = 'http://0.0.0.0:' + port + '/comet';
|
25
23
|
var comet = this._comet = new faye.NodeAdapter({mount: '/comet', timeout: 30});
|
26
24
|
this._server = http.createServer(function(request, response) {
|
27
25
|
comet.call(request, response);
|
28
26
|
});
|
29
27
|
this._server.listen(port);
|
28
|
+
Continue();
|
29
|
+
},
|
30
|
+
|
31
|
+
killServer: function(Continue) {
|
32
|
+
if (this._server) this._server.close();
|
33
|
+
this._server = undefined;
|
34
|
+
setTimeout(Continue, 500);
|
30
35
|
},
|
31
36
|
|
32
|
-
httpClient: function(name, channels) {
|
33
|
-
this._setupClient(new faye.Client(this._endpoint), name, channels);
|
37
|
+
httpClient: function(name, channels, Continue) {
|
38
|
+
this._setupClient(new faye.Client(this._endpoint, {timeout: 5}), name, channels, Continue);
|
34
39
|
},
|
35
40
|
|
36
|
-
localClient: function(name, channels) {
|
37
|
-
this._setupClient(this._comet.getClient(), name, channels);
|
41
|
+
localClient: function(name, channels, Continue) {
|
42
|
+
this._setupClient(this._comet.getClient(), name, channels, Continue);
|
38
43
|
},
|
39
44
|
|
40
|
-
_setupClient: function(client, name, channels) {
|
45
|
+
_setupClient: function(client, name, channels, Continue) {
|
41
46
|
Faye.each(channels, function(channel) {
|
42
|
-
sys.puts('Client ' + name + ' subscribing to ' + channel);
|
43
47
|
client.subscribe(channel, function(message) {
|
44
48
|
var box = this._inbox[name];
|
45
49
|
box[channel] = box[channel] || [];
|
@@ -49,35 +53,67 @@ Scenario = Faye.Class({
|
|
49
53
|
this._clients[name] = client;
|
50
54
|
this._inbox[name] = {};
|
51
55
|
this._pool += 1;
|
56
|
+
setTimeout(Continue, 500 * channels.length);
|
52
57
|
},
|
53
58
|
|
54
|
-
|
55
|
-
|
56
|
-
setTimeout(
|
57
|
-
var displayMessage = JSON.stringify(message);
|
58
|
-
sys.puts('Client ' + from + ' publishing ' + displayMessage + ' to ' + channel);
|
59
|
-
self._clients[from].publish(channel, message);
|
60
|
-
}, 500);
|
59
|
+
publish: function(from, channel, message, Continue) {
|
60
|
+
this._clients[from].publish(channel, message);
|
61
|
+
setTimeout(Continue, 500);
|
61
62
|
},
|
62
63
|
|
63
|
-
checkInbox: function(expectedInbox) {
|
64
|
-
|
65
|
-
|
66
|
-
self._checkInbox(expectedInbox);
|
67
|
-
sys.puts('Shutting down server\n');
|
68
|
-
Faye.each(this._clients, function(name, client) { client.disconnect() });
|
69
|
-
self._server.close();
|
70
|
-
Scenario.runNext();
|
71
|
-
}, 1000);
|
64
|
+
checkInbox: function(expectedInbox, Continue) {
|
65
|
+
assert.deepEqual(this._inbox, expectedInbox);
|
66
|
+
Continue();
|
72
67
|
},
|
73
68
|
|
74
|
-
|
75
|
-
|
76
|
-
|
69
|
+
finish: function(Continue) {
|
70
|
+
Faye.each(this._clients, function(name, client) { client.disconnect() });
|
71
|
+
this._server.close();
|
72
|
+
Continue();
|
73
|
+
},
|
74
|
+
});
|
75
|
+
|
76
|
+
SyncScenario = Faye.Class({
|
77
|
+
initialize: function(name, block) {
|
78
|
+
this._name = name;
|
79
|
+
this._commands = [];
|
80
|
+
this._scenario = new AsyncScenario();
|
81
|
+
block.call(this);
|
82
|
+
},
|
83
|
+
|
84
|
+
run: function() {
|
85
|
+
sys.puts('\n' + this._name);
|
86
|
+
sys.puts('----------------------------------------------------------------');
|
87
|
+
this._runNextCommand();
|
88
|
+
},
|
89
|
+
|
90
|
+
_runNextCommand: function() {
|
91
|
+
if (this._commands.length === 0) return this._finish();
|
92
|
+
|
93
|
+
var command = this._commands.shift(),
|
94
|
+
method = command[0],
|
95
|
+
args = Array.prototype.slice.call(command[1]),
|
96
|
+
self = this;
|
97
|
+
|
98
|
+
this._scenario[method].apply(this._scenario, args.concat(function() {
|
99
|
+
self._runNextCommand();
|
100
|
+
}));
|
101
|
+
},
|
102
|
+
|
103
|
+
_finish: function() {
|
104
|
+
sys.puts('No errors; Shutting down server\n');
|
105
|
+
this._scenario.finish(function() { SyncScenario.runNext() });
|
106
|
+
}
|
107
|
+
});
|
108
|
+
|
109
|
+
['wait', 'server', 'killServer', 'httpClient', 'localClient', 'publish', 'checkInbox'].
|
110
|
+
forEach(function(method) {
|
111
|
+
SyncScenario.prototype[method] = function() {
|
112
|
+
this._commands.push([method, arguments]);
|
77
113
|
}
|
78
114
|
});
|
79
115
|
|
80
|
-
Faye.extend(
|
116
|
+
Faye.extend(SyncScenario, {
|
81
117
|
_queue: [],
|
82
118
|
|
83
119
|
enqueue: function(name, block) {
|
@@ -93,7 +129,7 @@ Faye.extend(Scenario, {
|
|
93
129
|
});
|
94
130
|
|
95
131
|
exports.run = function(name, block) {
|
96
|
-
|
97
|
-
if (!
|
132
|
+
SyncScenario.enqueue(name, block);
|
133
|
+
if (!SyncScenario.running) SyncScenario.runNext();
|
98
134
|
};
|
99
135
|
|
data/test/scenario.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
$VERBOSE = false
|
2
|
+
|
3
|
+
module Scenario
|
4
|
+
module ClassMethods
|
5
|
+
def scenario(name, &block)
|
6
|
+
define_method("test: #{name}", &block)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.included(klass)
|
11
|
+
klass.extend ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup
|
15
|
+
Thread.new { EM.run }
|
16
|
+
while not EM.reactor_running?; end
|
17
|
+
@scenario = AsyncScenario.new
|
18
|
+
@commands = []
|
19
|
+
@started = false
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
while EM.reactor_running?; end
|
24
|
+
end
|
25
|
+
|
26
|
+
def run(runner)
|
27
|
+
@runner = runner
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(sym, *args)
|
32
|
+
@commands << [sym, args]
|
33
|
+
EM.next_tick { run_next_command unless @started }
|
34
|
+
end
|
35
|
+
|
36
|
+
def run_next_command
|
37
|
+
@started = true
|
38
|
+
command = @commands.shift
|
39
|
+
return EM.stop if command.nil?
|
40
|
+
begin
|
41
|
+
@scenario.__send__(command.first, *command.last) do
|
42
|
+
run_next_command
|
43
|
+
end
|
44
|
+
rescue Object => e
|
45
|
+
@passed = false
|
46
|
+
add_failure(e.message, e.backtrace)
|
47
|
+
@runner.puke(self.class, self.name, e) if @runner.respond_to?(:puke)
|
48
|
+
block.call
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class AsyncScenario
|
53
|
+
include Test::Unit::Assertions
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
@clients = {}
|
57
|
+
@inbox = {}
|
58
|
+
@pool = 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def check_inbox(expected_inbox, &block)
|
62
|
+
assert_equal expected_inbox, @inbox
|
63
|
+
block.call
|
64
|
+
end
|
65
|
+
|
66
|
+
def server(port, &block)
|
67
|
+
@endpoint = "http://0.0.0.0:#{port}/comet"
|
68
|
+
@comet = Faye::RackAdapter.new(:mount => '/comet', :timeout => 30)
|
69
|
+
Rack::Handler.get('thin').run(@comet, :Port => port) do |server|
|
70
|
+
@server = server
|
71
|
+
EM.next_tick(&block)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def http_client(name, channels, &block)
|
76
|
+
setup_client(Faye::Client.new(@endpoint), name, channels, &block)
|
77
|
+
end
|
78
|
+
|
79
|
+
def local_client(name, channels, &block)
|
80
|
+
setup_client(@comet.get_client, name, channels, &block)
|
81
|
+
end
|
82
|
+
|
83
|
+
def setup_client(client, name, channels, &block)
|
84
|
+
channels.each do |channel|
|
85
|
+
client.subscribe(channel) do |message|
|
86
|
+
box = @inbox[name]
|
87
|
+
box[channel] ||= []
|
88
|
+
box[channel] << message
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@clients[name] = client
|
92
|
+
@inbox[name] = {}
|
93
|
+
@pool += 1
|
94
|
+
EM.add_timer(0.5 * channels.size, &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
def publish(from, channel, message, &block)
|
98
|
+
@clients[from].publish(channel, message)
|
99
|
+
EM.add_timer(2, &block)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
data/test/test_channel.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require "test/unit"
|
2
|
-
require "faye"
|
2
|
+
require File.dirname(__FILE__) + "/../lib/faye"
|
3
3
|
|
4
4
|
class TestChannel < Test::Unit::TestCase
|
5
5
|
include Faye
|
@@ -15,6 +15,12 @@ class TestChannel < Test::Unit::TestCase
|
|
15
15
|
assert_equal 3, tree['/va()$$lid/name']
|
16
16
|
end
|
17
17
|
|
18
|
+
def test_keys
|
19
|
+
tree = Channel::Tree.new
|
20
|
+
tree['/foo'] = tree['/bar'] = tree['/foo/bar'] = true
|
21
|
+
assert_equal %w[/bar /foo /foo/bar], tree.keys.sort
|
22
|
+
end
|
23
|
+
|
18
24
|
def test_globbing
|
19
25
|
tree = Channel::Tree.new
|
20
26
|
tree['/foo/bar'] = 1
|
data/test/test_clients.js
CHANGED
@@ -20,12 +20,30 @@ var Scenario = require('./scenario'),
|
|
20
20
|
assert.deepEqual(tree.glob('/channels/**').sort(), ['A','B','C']);
|
21
21
|
})();
|
22
22
|
|
23
|
+
Scenario.run("Server goes away, subscriptions should be revived",
|
24
|
+
function() { with(this) {
|
25
|
+
server(8000);
|
26
|
+
httpClient('A', ['/channels/a']);
|
27
|
+
httpClient('B', []);
|
28
|
+
killServer();
|
29
|
+
wait(6);
|
30
|
+
server(8000);
|
31
|
+
wait(22);
|
32
|
+
publish('B', '/channels/a', {hello: 'world'});
|
33
|
+
checkInbox({
|
34
|
+
A: {
|
35
|
+
'/channels/a': [{hello: 'world'}]
|
36
|
+
},
|
37
|
+
B: {}
|
38
|
+
});
|
39
|
+
}});
|
40
|
+
|
23
41
|
Scenario.run("Two HTTP clients, no messages delivered",
|
24
42
|
function() { with(this) {
|
25
43
|
server(8000);
|
26
44
|
httpClient('A', ['/channels/a']);
|
27
45
|
httpClient('B', []);
|
28
|
-
|
46
|
+
publish('B', '/channels/b', {hello: 'world'});
|
29
47
|
checkInbox({
|
30
48
|
A: {},
|
31
49
|
B: {}
|
@@ -37,7 +55,7 @@ function() { with(this) {
|
|
37
55
|
server(8000);
|
38
56
|
httpClient('A', ['/channels/a']);
|
39
57
|
httpClient('B', []);
|
40
|
-
|
58
|
+
publish('B', '/channels/a', {hello: 'world'});
|
41
59
|
checkInbox({
|
42
60
|
A: {
|
43
61
|
'/channels/a': [{hello: 'world'}]
|
@@ -51,7 +69,7 @@ function() { with(this) {
|
|
51
69
|
server(8000);
|
52
70
|
httpClient('A', ['/channels/a', '/channels/*']);
|
53
71
|
httpClient('B', []);
|
54
|
-
|
72
|
+
publish('B', '/channels/a', {hello: 'world'});
|
55
73
|
checkInbox({
|
56
74
|
A: {
|
57
75
|
'/channels/a': [{hello: 'world'}],
|
@@ -67,7 +85,7 @@ function() { with(this) {
|
|
67
85
|
httpClient('A', ['/channels/a']);
|
68
86
|
httpClient('B', []);
|
69
87
|
httpClient('C', ['/channels/c']);
|
70
|
-
|
88
|
+
publish('B', '/channels/a', {chunky: 'bacon'});
|
71
89
|
checkInbox({
|
72
90
|
A: {
|
73
91
|
'/channels/a': [{chunky: 'bacon'}]
|
@@ -83,7 +101,7 @@ function() { with(this) {
|
|
83
101
|
httpClient('A', ['/channels/shared']);
|
84
102
|
httpClient('B', []);
|
85
103
|
httpClient('C', ['/channels/shared']);
|
86
|
-
|
104
|
+
publish('B', '/channels/shared', {chunky: 'bacon'});
|
87
105
|
checkInbox({
|
88
106
|
A: {
|
89
107
|
'/channels/shared': [{chunky: 'bacon'}]
|
@@ -100,7 +118,7 @@ function() { with(this) {
|
|
100
118
|
server(8000);
|
101
119
|
httpClient('A', ['/channels/*']);
|
102
120
|
httpClient('B', []);
|
103
|
-
|
121
|
+
publish('B', '/channels/anything', {msg: 'hey'});
|
104
122
|
checkInbox({
|
105
123
|
A: {
|
106
124
|
'/channels/*': [{msg: 'hey'}]
|
@@ -114,7 +132,7 @@ function() { with(this) {
|
|
114
132
|
server(8000);
|
115
133
|
httpClient('A', ['/channels/name', '/channels/hello', '/channels/nested/hello']);
|
116
134
|
httpClient('B', []);
|
117
|
-
|
135
|
+
publish('B', '/channels/*', {msg: 'hey'});
|
118
136
|
checkInbox({
|
119
137
|
A: {
|
120
138
|
'/channels/name': [{msg: 'hey'}],
|
@@ -129,7 +147,7 @@ function() { with(this) {
|
|
129
147
|
server(8000);
|
130
148
|
httpClient('A', ['/channels/*']);
|
131
149
|
httpClient('B', []);
|
132
|
-
|
150
|
+
publish('B', '/channels/*', {msg: 'hey'});
|
133
151
|
checkInbox({
|
134
152
|
A: {
|
135
153
|
'/channels/*': [{msg: 'hey'}]
|
@@ -143,7 +161,7 @@ function() { with(this) {
|
|
143
161
|
server(8000);
|
144
162
|
localClient('A', ['/channels/name', '/channels/hello', '/channels/nested/hello']);
|
145
163
|
localClient('B', []);
|
146
|
-
|
164
|
+
publish('B', '/channels/**', {msg: 'hey'});
|
147
165
|
checkInbox({
|
148
166
|
A: {
|
149
167
|
'/channels/name': [{msg: 'hey'}],
|
@@ -160,7 +178,7 @@ function() { with(this) {
|
|
160
178
|
localClient('A', ['/channels/hello', '/channels/nested/hello']);
|
161
179
|
localClient('B', []);
|
162
180
|
httpClient('C', ['/channels/name', '/channels/foo/**']);
|
163
|
-
|
181
|
+
publish('B', '/channels/**', {msg: 'hey'});
|
164
182
|
checkInbox({
|
165
183
|
A: {
|
166
184
|
'/channels/hello': [{msg: 'hey'}],
|