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.
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