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.
Files changed (50) hide show
  1. data/CHANGELOG.md +22 -0
  2. data/Gemfile +4 -0
  3. data/README.md +66 -159
  4. data/Rakefile +31 -4
  5. data/bin/thin-socketrails +16 -1
  6. data/lib/assets/javascripts/websocket_rails/channel.js.coffee +23 -8
  7. data/lib/assets/javascripts/websocket_rails/event.js.coffee +40 -0
  8. data/lib/assets/javascripts/websocket_rails/http_connection.js.coffee +18 -10
  9. data/lib/assets/javascripts/websocket_rails/main.js +1 -0
  10. data/lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee +15 -10
  11. data/lib/assets/javascripts/websocket_rails/websocket_rails.js.coffee +41 -23
  12. data/lib/websocket-rails.rb +4 -4
  13. data/lib/websocket_rails/base_controller.rb +61 -29
  14. data/lib/websocket_rails/channel.rb +14 -5
  15. data/lib/websocket_rails/channel_manager.rb +3 -1
  16. data/lib/websocket_rails/connection_adapters.rb +34 -12
  17. data/lib/websocket_rails/connection_manager.rb +4 -0
  18. data/lib/websocket_rails/dispatcher.rb +27 -3
  19. data/lib/websocket_rails/engine.rb +2 -5
  20. data/lib/websocket_rails/event.rb +87 -42
  21. data/lib/websocket_rails/event_map.rb +70 -20
  22. data/lib/websocket_rails/event_queue.rb +4 -0
  23. data/lib/websocket_rails/internal_events.rb +21 -3
  24. data/lib/websocket_rails/logging.rb +18 -0
  25. data/lib/websocket_rails/version.rb +1 -1
  26. data/spec/dummy/log/test.log +0 -429
  27. data/spec/integration/connection_manager_spec.rb +3 -5
  28. data/spec/javascripts/generated/assets/channel.js +98 -0
  29. data/spec/javascripts/generated/assets/event.js +78 -0
  30. data/spec/javascripts/generated/assets/http_connection.js +108 -0
  31. data/spec/javascripts/generated/assets/websocket_connection.js +66 -0
  32. data/spec/javascripts/generated/assets/websocket_rails.js +180 -0
  33. data/spec/javascripts/generated/specs/channel_spec.js +66 -0
  34. data/spec/javascripts/generated/specs/event_spec.js +107 -0
  35. data/spec/javascripts/generated/specs/websocket_connection_spec.js +117 -0
  36. data/spec/javascripts/generated/specs/websocket_rails_spec.js +232 -0
  37. data/spec/javascripts/support/jasmine.yml +44 -0
  38. data/spec/javascripts/support/jasmine_config.rb +63 -0
  39. data/spec/javascripts/support/vendor/sinon-1.3.4.js +3555 -0
  40. data/spec/javascripts/websocket_rails/channel_spec.coffee +51 -0
  41. data/spec/javascripts/websocket_rails/event_spec.coffee +69 -0
  42. data/spec/javascripts/websocket_rails/websocket_connection_spec.coffee +86 -0
  43. data/spec/javascripts/websocket_rails/websocket_rails_spec.coffee +166 -0
  44. data/spec/support/helper_methods.rb +10 -1
  45. data/spec/unit/channel_spec.rb +28 -4
  46. data/spec/unit/connection_adapters_spec.rb +17 -0
  47. data/spec/unit/connection_manager_spec.rb +1 -1
  48. data/spec/unit/dispatcher_spec.rb +1 -1
  49. data/spec/unit/event_spec.rb +15 -11
  50. 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