websocket-rails 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|