websocket-rails 0.1.5 → 0.1.6
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.
- data/CHANGELOG.md +22 -0
- data/Gemfile +4 -0
- data/README.md +66 -159
- data/Rakefile +31 -4
- data/bin/thin-socketrails +16 -1
- data/lib/assets/javascripts/websocket_rails/channel.js.coffee +23 -8
- data/lib/assets/javascripts/websocket_rails/event.js.coffee +40 -0
- data/lib/assets/javascripts/websocket_rails/http_connection.js.coffee +18 -10
- data/lib/assets/javascripts/websocket_rails/main.js +1 -0
- data/lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee +15 -10
- data/lib/assets/javascripts/websocket_rails/websocket_rails.js.coffee +41 -23
- data/lib/websocket-rails.rb +4 -4
- data/lib/websocket_rails/base_controller.rb +61 -29
- data/lib/websocket_rails/channel.rb +14 -5
- data/lib/websocket_rails/channel_manager.rb +3 -1
- data/lib/websocket_rails/connection_adapters.rb +34 -12
- data/lib/websocket_rails/connection_manager.rb +4 -0
- data/lib/websocket_rails/dispatcher.rb +27 -3
- data/lib/websocket_rails/engine.rb +2 -5
- data/lib/websocket_rails/event.rb +87 -42
- data/lib/websocket_rails/event_map.rb +70 -20
- data/lib/websocket_rails/event_queue.rb +4 -0
- data/lib/websocket_rails/internal_events.rb +21 -3
- data/lib/websocket_rails/logging.rb +18 -0
- data/lib/websocket_rails/version.rb +1 -1
- data/spec/dummy/log/test.log +0 -429
- data/spec/integration/connection_manager_spec.rb +3 -5
- data/spec/javascripts/generated/assets/channel.js +98 -0
- data/spec/javascripts/generated/assets/event.js +78 -0
- data/spec/javascripts/generated/assets/http_connection.js +108 -0
- data/spec/javascripts/generated/assets/websocket_connection.js +66 -0
- data/spec/javascripts/generated/assets/websocket_rails.js +180 -0
- data/spec/javascripts/generated/specs/channel_spec.js +66 -0
- data/spec/javascripts/generated/specs/event_spec.js +107 -0
- data/spec/javascripts/generated/specs/websocket_connection_spec.js +117 -0
- data/spec/javascripts/generated/specs/websocket_rails_spec.js +232 -0
- data/spec/javascripts/support/jasmine.yml +44 -0
- data/spec/javascripts/support/jasmine_config.rb +63 -0
- data/spec/javascripts/support/vendor/sinon-1.3.4.js +3555 -0
- data/spec/javascripts/websocket_rails/channel_spec.coffee +51 -0
- data/spec/javascripts/websocket_rails/event_spec.coffee +69 -0
- data/spec/javascripts/websocket_rails/websocket_connection_spec.coffee +86 -0
- data/spec/javascripts/websocket_rails/websocket_rails_spec.coffee +166 -0
- data/spec/support/helper_methods.rb +10 -1
- data/spec/unit/channel_spec.rb +28 -4
- data/spec/unit/connection_adapters_spec.rb +17 -0
- data/spec/unit/connection_manager_spec.rb +1 -1
- data/spec/unit/dispatcher_spec.rb +1 -1
- data/spec/unit/event_spec.rb +15 -11
- metadata +22 -4
@@ -0,0 +1,232 @@
|
|
1
|
+
(function() {
|
2
|
+
|
3
|
+
describe('WebSocketRails:', function() {
|
4
|
+
beforeEach(function() {
|
5
|
+
this.url = 'localhost:3000/websocket';
|
6
|
+
WebSocketRails.WebSocketConnection = function() {
|
7
|
+
return {
|
8
|
+
connection_type: 'websocket',
|
9
|
+
flush_queue: function() {
|
10
|
+
return true;
|
11
|
+
}
|
12
|
+
};
|
13
|
+
};
|
14
|
+
WebSocketRails.HttpConnection = function() {
|
15
|
+
return {
|
16
|
+
connection_type: 'http',
|
17
|
+
flush_queue: function() {
|
18
|
+
return true;
|
19
|
+
}
|
20
|
+
};
|
21
|
+
};
|
22
|
+
return this.dispatcher = new WebSocketRails(this.url);
|
23
|
+
});
|
24
|
+
describe('constructor', function() {
|
25
|
+
it('should set the new_message method on connection to this.new_message', function() {
|
26
|
+
return expect(this.dispatcher._conn.new_message).toEqual(this.dispatcher.new_message);
|
27
|
+
});
|
28
|
+
it('should set the initial state to connecting', function() {
|
29
|
+
return expect(this.dispatcher.state).toEqual('connecting');
|
30
|
+
});
|
31
|
+
describe('when use_websockets is true', function() {
|
32
|
+
return it('should use the WebSocket Connection', function() {
|
33
|
+
var dispatcher;
|
34
|
+
dispatcher = new WebSocketRails(this.url, true);
|
35
|
+
return expect(dispatcher._conn.connection_type).toEqual('websocket');
|
36
|
+
});
|
37
|
+
});
|
38
|
+
describe('when use_webosckets is false', function() {
|
39
|
+
return it('should use the Http Connection', function() {
|
40
|
+
var dispatcher;
|
41
|
+
dispatcher = new WebSocketRails(this.url, false);
|
42
|
+
return expect(dispatcher._conn.connection_type).toEqual('http');
|
43
|
+
});
|
44
|
+
});
|
45
|
+
return describe('when the browser does not support WebSockets', function() {
|
46
|
+
return it('should use the Http Connection', function() {
|
47
|
+
var dispatcher;
|
48
|
+
window.WebSocket = 'undefined';
|
49
|
+
dispatcher = new WebSocketRails(this.url, true);
|
50
|
+
return expect(dispatcher._conn.connection_type).toEqual('http');
|
51
|
+
});
|
52
|
+
});
|
53
|
+
});
|
54
|
+
describe('.new_message', function() {
|
55
|
+
describe('when this.state is "connecting"', function() {
|
56
|
+
beforeEach(function() {
|
57
|
+
this.message = {
|
58
|
+
data: {
|
59
|
+
connection_id: 123
|
60
|
+
}
|
61
|
+
};
|
62
|
+
return this.data = [['client_connected', this.message]];
|
63
|
+
});
|
64
|
+
it('should call this.connection_established on the "client_connected" event', function() {
|
65
|
+
var mock_dispatcher;
|
66
|
+
mock_dispatcher = sinon.mock(this.dispatcher);
|
67
|
+
mock_dispatcher.expects('connection_established').once().withArgs(this.message.data);
|
68
|
+
this.dispatcher.new_message(this.data);
|
69
|
+
return mock_dispatcher.verify();
|
70
|
+
});
|
71
|
+
it('should set the state to connected', function() {
|
72
|
+
this.dispatcher.new_message(this.data);
|
73
|
+
return expect(this.dispatcher.state).toEqual('connected');
|
74
|
+
});
|
75
|
+
it('should flush any messages queued before the connection was established', function() {
|
76
|
+
var mock_con;
|
77
|
+
mock_con = sinon.mock(this.dispatcher._conn);
|
78
|
+
mock_con.expects('flush_queue').once();
|
79
|
+
this.dispatcher.new_message(this.data);
|
80
|
+
return mock_con.verify();
|
81
|
+
});
|
82
|
+
it('should set the correct connection_id', function() {
|
83
|
+
this.dispatcher.new_message(this.data);
|
84
|
+
return expect(this.dispatcher.connection_id).toEqual(123);
|
85
|
+
});
|
86
|
+
return it('should call the user defined on_open callback', function() {
|
87
|
+
var spy;
|
88
|
+
spy = sinon.spy();
|
89
|
+
this.dispatcher.on_open = spy;
|
90
|
+
this.dispatcher.new_message(this.data);
|
91
|
+
return expect(spy.calledOnce).toEqual(true);
|
92
|
+
});
|
93
|
+
});
|
94
|
+
return describe('after the connection has been established', function() {
|
95
|
+
beforeEach(function() {
|
96
|
+
this.dispatcher.state = 'connected';
|
97
|
+
return this.attributes = {
|
98
|
+
data: 'message',
|
99
|
+
channel: 'channel'
|
100
|
+
};
|
101
|
+
});
|
102
|
+
it('should dispatch channel messages', function() {
|
103
|
+
var data, mock_dispatcher;
|
104
|
+
data = [['event', this.attributes]];
|
105
|
+
mock_dispatcher = sinon.mock(this.dispatcher);
|
106
|
+
mock_dispatcher.expects('dispatch_channel').once();
|
107
|
+
this.dispatcher.new_message(data);
|
108
|
+
return mock_dispatcher.verify();
|
109
|
+
});
|
110
|
+
it('should dispatch standard events', function() {
|
111
|
+
var data, mock_dispatcher;
|
112
|
+
data = [['event', 'message']];
|
113
|
+
mock_dispatcher = sinon.mock(this.dispatcher);
|
114
|
+
mock_dispatcher.expects('dispatch').once();
|
115
|
+
this.dispatcher.new_message(data);
|
116
|
+
return mock_dispatcher.verify();
|
117
|
+
});
|
118
|
+
return describe('result events', function() {
|
119
|
+
beforeEach(function() {
|
120
|
+
this.attributes['success'] = true;
|
121
|
+
this.attributes['id'] = 1;
|
122
|
+
this.event = {
|
123
|
+
run_callbacks: function(data) {}
|
124
|
+
};
|
125
|
+
this.event_mock = sinon.mock(this.event);
|
126
|
+
return this.dispatcher.queue[1] = this.event;
|
127
|
+
});
|
128
|
+
return it('should run callbacks for result events', function() {
|
129
|
+
var data;
|
130
|
+
data = [['event', this.attributes]];
|
131
|
+
this.event_mock.expects('run_callbacks').once();
|
132
|
+
this.dispatcher.new_message(data);
|
133
|
+
return this.event_mock.verify();
|
134
|
+
});
|
135
|
+
});
|
136
|
+
});
|
137
|
+
});
|
138
|
+
describe('.bind', function() {
|
139
|
+
return it('should store the callback on the correct event', function() {
|
140
|
+
var callback;
|
141
|
+
callback = function() {};
|
142
|
+
this.dispatcher.bind('event', callback);
|
143
|
+
return expect(this.dispatcher.callbacks['event']).toContain(callback);
|
144
|
+
});
|
145
|
+
});
|
146
|
+
describe('.dispatch', function() {
|
147
|
+
return it('should execute the callback for the correct event', function() {
|
148
|
+
var callback, event;
|
149
|
+
callback = sinon.spy();
|
150
|
+
event = new WebSocketRails.Event([
|
151
|
+
'event', {
|
152
|
+
data: 'message'
|
153
|
+
}
|
154
|
+
]);
|
155
|
+
this.dispatcher.bind('event', callback);
|
156
|
+
this.dispatcher.dispatch(event);
|
157
|
+
return expect(callback.calledWith('message')).toEqual(true);
|
158
|
+
});
|
159
|
+
});
|
160
|
+
describe('triggering events with', function() {
|
161
|
+
beforeEach(function() {
|
162
|
+
this.dispatcher.connection_id = 123;
|
163
|
+
return this.dispatcher._conn = {
|
164
|
+
trigger: function() {},
|
165
|
+
trigger_channel: function() {}
|
166
|
+
};
|
167
|
+
});
|
168
|
+
return describe('.trigger', function() {
|
169
|
+
return it('should delegate to the connection object', function() {
|
170
|
+
var con_trigger, event;
|
171
|
+
con_trigger = sinon.spy(this.dispatcher._conn, 'trigger');
|
172
|
+
this.dispatcher.trigger('event', 'message');
|
173
|
+
event = new WebSocketRails.Event([
|
174
|
+
'websocket_rails.subscribe', {
|
175
|
+
channel: 'awesome'
|
176
|
+
}, 123
|
177
|
+
]);
|
178
|
+
return expect(con_trigger.called).toEqual(true);
|
179
|
+
});
|
180
|
+
});
|
181
|
+
});
|
182
|
+
return describe('working with channels', function() {
|
183
|
+
beforeEach(function() {
|
184
|
+
return WebSocketRails.Channel = function(name, dispatcher, is_private) {
|
185
|
+
this.name = name;
|
186
|
+
this.dispatcher = dispatcher;
|
187
|
+
this.is_private = is_private;
|
188
|
+
};
|
189
|
+
});
|
190
|
+
describe('.subscribe', function() {
|
191
|
+
describe('for new channels', function() {
|
192
|
+
return it('should create and store a new Channel object', function() {
|
193
|
+
var channel;
|
194
|
+
channel = this.dispatcher.subscribe('test_channel');
|
195
|
+
return expect(channel.name).toEqual('test_channel');
|
196
|
+
});
|
197
|
+
});
|
198
|
+
return describe('for existing channels', function() {
|
199
|
+
return it('should return the same Channel object', function() {
|
200
|
+
var channel;
|
201
|
+
channel = this.dispatcher.subscribe('test_channel');
|
202
|
+
return expect(this.dispatcher.subscribe('test_channel')).toEqual(channel);
|
203
|
+
});
|
204
|
+
});
|
205
|
+
});
|
206
|
+
describe('.subscribe_private', function() {
|
207
|
+
return it('should create private channels', function() {
|
208
|
+
var private_channel;
|
209
|
+
private_channel = this.dispatcher.subscribe_private('private_something');
|
210
|
+
return expect(private_channel.is_private).toBe(true);
|
211
|
+
});
|
212
|
+
});
|
213
|
+
return describe('.dispatch_channel', function() {
|
214
|
+
return it('should delegate to the Channel object', function() {
|
215
|
+
var channel, event, spy;
|
216
|
+
channel = this.dispatcher.subscribe('test');
|
217
|
+
channel.dispatch = function() {};
|
218
|
+
spy = sinon.spy(channel, 'dispatch');
|
219
|
+
event = new WebSocketRails.Event([
|
220
|
+
'event', {
|
221
|
+
channel: 'test',
|
222
|
+
data: 'awesome'
|
223
|
+
}
|
224
|
+
]);
|
225
|
+
this.dispatcher.dispatch_channel(event);
|
226
|
+
return expect(spy.calledWith('event', 'awesome')).toEqual(true);
|
227
|
+
});
|
228
|
+
});
|
229
|
+
});
|
230
|
+
});
|
231
|
+
|
232
|
+
}).call(this);
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Return an array of filepaths relative to src_dir to include before jasmine specs.
|
2
|
+
# Default: []
|
3
|
+
#
|
4
|
+
# EXAMPLE:
|
5
|
+
#
|
6
|
+
# src_files:
|
7
|
+
# - lib/source1.js
|
8
|
+
# - lib/source2.js
|
9
|
+
# - dist/**/*.js
|
10
|
+
#
|
11
|
+
src_dir: spec/javascripts
|
12
|
+
src_files:
|
13
|
+
- support/vendor/sinon-1.3.4.js
|
14
|
+
- generated/assets/websocket_rails.js
|
15
|
+
- generated/assets/*.js
|
16
|
+
|
17
|
+
spec_dir: spec/javascripts/generated
|
18
|
+
spec_files:
|
19
|
+
- specs/*_spec.js
|
20
|
+
|
21
|
+
# stylesheets
|
22
|
+
#
|
23
|
+
# Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs.
|
24
|
+
# Default: []
|
25
|
+
#
|
26
|
+
# EXAMPLE:
|
27
|
+
#
|
28
|
+
# stylesheets:
|
29
|
+
# - css/style.css
|
30
|
+
# - stylesheets/*.css
|
31
|
+
#
|
32
|
+
stylesheets:
|
33
|
+
|
34
|
+
# helpers
|
35
|
+
#
|
36
|
+
# Return an array of filepaths relative to spec_dir to include before jasmine specs.
|
37
|
+
# Default: ["helpers/**/*.js"]
|
38
|
+
#
|
39
|
+
# EXAMPLE:
|
40
|
+
#
|
41
|
+
# helpers:
|
42
|
+
# - helpers/**/*.js
|
43
|
+
#
|
44
|
+
helpers:
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# spec/javascripts/support/jasmine_config.rb
|
2
|
+
# when jasmine starts the server out-of-process, it needs this in order to be able to invoke the asset tasks
|
3
|
+
unless Object.const_defined?(:Rake)
|
4
|
+
require 'rake'
|
5
|
+
load File.expand_path('../../../../Rakefile', __FILE__)
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'coffee_script'
|
9
|
+
|
10
|
+
module Jasmine
|
11
|
+
class Config
|
12
|
+
include Rake::DSL
|
13
|
+
|
14
|
+
def js_files(spec_filter = nil)
|
15
|
+
# remove all generated files
|
16
|
+
generated_files_directory = File.expand_path("../../../../spec/javascripts/generated/assets", __FILE__)
|
17
|
+
rm_rf generated_files_directory, :secure => true
|
18
|
+
|
19
|
+
precompile_app_assets
|
20
|
+
compile_jasmine_javascripts
|
21
|
+
|
22
|
+
# this is code from the original jasmine config js_files method - you could also just alias_method_chain it
|
23
|
+
spec_files_to_include = spec_filter.nil? ? spec_files : match_files(spec_dir, [spec_filter])
|
24
|
+
src_files.collect {|f| "/" + f } + helpers.collect {|f| File.join(spec_path, f) } + spec_files_to_include.collect {|f| File.join(spec_path, f) }
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# this method compiles all the same javascript files your app will
|
30
|
+
def precompile_app_assets
|
31
|
+
puts "Precompiling assets..."
|
32
|
+
|
33
|
+
root = File.expand_path("../../../../lib/assets/javascripts/websocket_rails", __FILE__)
|
34
|
+
destination_dir = File.expand_path("../../../../spec/javascripts/generated/assets", __FILE__)
|
35
|
+
|
36
|
+
glob = File.expand_path("**/*.js.coffee", root)
|
37
|
+
|
38
|
+
Dir.glob(glob).each do |srcfile|
|
39
|
+
srcfile = Pathname.new(srcfile)
|
40
|
+
destfile = srcfile.sub(root, destination_dir).sub(".coffee", "")
|
41
|
+
FileUtils.mkdir_p(destfile.dirname)
|
42
|
+
File.open(destfile, "w") {|f| f.write(CoffeeScript.compile(File.new(srcfile)))}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# this method compiles all of the spec files into js files that jasmine can run
|
47
|
+
def compile_jasmine_javascripts
|
48
|
+
puts "Compiling jasmine coffee scripts into javascript..."
|
49
|
+
root = File.expand_path("../../../../spec/javascripts/websocket_rails", __FILE__)
|
50
|
+
destination_dir = File.expand_path("../../generated/specs", __FILE__)
|
51
|
+
|
52
|
+
glob = File.expand_path("**/*.coffee", root)
|
53
|
+
|
54
|
+
Dir.glob(glob).each do |srcfile|
|
55
|
+
srcfile = Pathname.new(srcfile)
|
56
|
+
destfile = srcfile.sub(root, destination_dir).sub(".coffee", ".js")
|
57
|
+
FileUtils.mkdir_p(destfile.dirname)
|
58
|
+
File.open(destfile, "w") {|f| f.write(CoffeeScript.compile(File.new(srcfile)))}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|