websocket-rails 0.4.7 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. data/CHANGELOG.md +13 -1
  2. data/Gemfile +1 -1
  3. data/lib/assets/javascripts/websocket_rails/channel.js.coffee +6 -0
  4. data/lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee +2 -2
  5. data/lib/assets/javascripts/websocket_rails/websocket_rails.js.coffee +5 -0
  6. data/lib/websocket_rails/base_controller.rb +3 -1
  7. data/lib/websocket_rails/connection_adapters/http.rb +4 -0
  8. data/lib/websocket_rails/connection_adapters/web_socket.rb +6 -2
  9. data/lib/websocket_rails/internal_events.rb +8 -1
  10. data/lib/websocket_rails/version.rb +1 -1
  11. data/spec/dummy/log/test.log +3 -0
  12. data/spec/javascripts/generated/assets/channel.js +17 -9
  13. data/spec/javascripts/generated/assets/event.js +0 -8
  14. data/spec/javascripts/generated/assets/http_connection.js +3 -8
  15. data/spec/javascripts/generated/assets/websocket_connection.js +2 -9
  16. data/spec/javascripts/generated/assets/websocket_rails.js +13 -17
  17. data/spec/javascripts/generated/specs/channel_spec.js +0 -1
  18. data/spec/javascripts/generated/specs/event_spec.js +0 -1
  19. data/spec/javascripts/generated/specs/websocket_connection_spec.js +26 -7
  20. data/spec/javascripts/generated/specs/websocket_rails_spec.js +8 -1
  21. data/spec/javascripts/support/jasmine.yml +1 -1
  22. data/spec/javascripts/support/vendor/sinon-1.7.1.js +4343 -0
  23. data/spec/javascripts/websocket_rails/websocket_connection_spec.coffee +28 -5
  24. data/spec/javascripts/websocket_rails/websocket_rails_spec.coffee +6 -0
  25. data/spec/unit/base_controller_spec.rb +33 -0
  26. data/spec/unit/connection_adapters/http_spec.rb +7 -0
  27. data/spec/unit/connection_adapters/web_socket_spec.rb +11 -4
  28. data/spec/unit/logging_spec.rb +2 -1
  29. metadata +7 -10
  30. data/spec/javascripts/generated/assets/upload_event.js +0 -56
  31. data/spec/javascripts/generated/specs/upload_event_spec.js +0 -55
  32. data/spec/javascripts/support/vendor/sinon-1.3.4.js +0 -3555
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # WebsocketRails Change Log
2
2
 
3
+ ## Version 0.4.8
4
+
5
+ July 6 2013
6
+
7
+ * Fix error with class reloading in development with Rails 4
8
+ * Added `connection.close!` method to allow for manually disconnecting users from a WebsocketRails controller.
9
+ * Add a way to unsubscribe from channels via the JavaScript client. - Thanks to @Oxynum
10
+ * Fix handling of `on_error` event in the JavaScript client. - Thanks to @imton
11
+
3
12
  ## Version 0.4.7
4
13
 
5
14
  June 6 2013
@@ -8,9 +17,12 @@ June 6 2013
8
17
  * Fix spelling mistake in ConnectionAdapters#inspect - Thanks to
9
18
  @bmxpert1
10
19
  * Prevent duplicate events from being triggered when events are added
11
- directly to Redis from an outside process. - Thanks to @DarkSwoop
20
+ directly to Redis from an outside process. - Thanks to @moaa
12
21
  * Only log event data if it is a Hash or String to drastically reduce
13
22
  the log file size. - Thanks to @florianguenther
23
+ * Fix the intermittent uninitialized constant
24
+ "WebsocketRails::InternalEvents" error in development. - Thanks to
25
+ @DarkSwoop
14
26
 
15
27
  ## Version 0.4.6
16
28
 
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gemspec
5
5
  gem "rspec-rails", ">=2.12.0"
6
6
  gem "therubyrhino"
7
7
  gem "therubyracer"
8
- gem "jasmine", ">=1.2.0"
8
+ gem "jasmine"
9
9
  gem "headless"
10
10
  gem "coffee-script"
11
11
  gem "thin"
@@ -19,6 +19,12 @@ class WebSocketRails.Channel
19
19
  @_dispatcher.trigger_event event
20
20
  @_callbacks = {}
21
21
 
22
+ destroy: () =>
23
+ event_name = 'websocket_rails.unsubscribe'
24
+ event = new WebSocketRails.Event( [event_name, {data: {channel: @name}}, @_dispatcher.connection_id] )
25
+ @_dispatcher.trigger_event event
26
+ @_callbacks = {}
27
+
22
28
  bind: (event_name, callback) =>
23
29
  @_callbacks[event_name] ?= []
24
30
  @_callbacks[event_name].push callback
@@ -22,11 +22,11 @@ class WebSocketRails.WebSocketConnection
22
22
  @dispatcher.new_message data
23
23
 
24
24
  on_close: (event) =>
25
- close_event = new WebSocketRails.Event(['connection_closed',{}])
25
+ close_event = new WebSocketRails.Event(['connection_closed', event])
26
26
  @dispatcher.dispatch close_event
27
27
 
28
28
  on_error: (event) =>
29
- error_event = new WebSocketRails.Event(['connection_error',event?.data])
29
+ error_event = new WebSocketRails.Event(['connection_error', event])
30
30
  @dispatcher.dispatch error_event
31
31
 
32
32
  flush_queue: =>
@@ -87,6 +87,11 @@ class window.WebSocketRails
87
87
  else
88
88
  @channels[channel_name]
89
89
 
90
+ unsubscribe: (channel_name) =>
91
+ return unless @channels[channel_name]?
92
+ @channels[channel_name].destroy()
93
+ delete @channels[channel_name]
94
+
90
95
  dispatch_channel: (event) =>
91
96
  return unless @channels[event.channel]?
92
97
  @channels[event.channel].dispatch event.name, event.data
@@ -21,7 +21,9 @@ module WebsocketRails
21
21
  # Tell Rails that BaseController and children can be reloaded when in
22
22
  # the Development environment.
23
23
  def self.inherited(controller)
24
- unloadable controller unless controller.name == "WebsocketRails::InternalController"
24
+ unless controller.name == "WebsocketRails::InternalController" || Rails.version =~/^4/
25
+ unloadable controller
26
+ end
25
27
  end
26
28
 
27
29
  # Add observers to specific events or the controller in general. This functionality is similar
@@ -31,6 +31,10 @@ module WebsocketRails
31
31
  @body.chunk encode_chunk( message )
32
32
  end
33
33
 
34
+ def close!
35
+ @body.close!
36
+ end
37
+
34
38
  private
35
39
 
36
40
  def define_deferrable_callbacks
@@ -6,9 +6,9 @@ module WebsocketRails
6
6
  Faye::WebSocket.websocket?( env )
7
7
  end
8
8
 
9
- def initialize(request,dispatcher)
9
+ def initialize(request, dispatcher)
10
10
  super
11
- @connection = Faye::WebSocket.new( request.env )
11
+ @connection = Faye::WebSocket.new(request.env)
12
12
  @connection.onmessage = method(:on_message)
13
13
  @connection.onerror = method(:on_error)
14
14
  @connection.onclose = method(:on_close)
@@ -24,6 +24,10 @@ module WebsocketRails
24
24
  super data
25
25
  end
26
26
 
27
+ def close!
28
+ @connection.close
29
+ end
30
+
27
31
  end
28
32
  end
29
33
  end
@@ -5,6 +5,7 @@ module WebsocketRails
5
5
  namespace :websocket_rails do
6
6
  subscribe :pong, :to => InternalController, :with_method => :do_pong
7
7
  subscribe :subscribe, :to => InternalController, :with_method => :subscribe_to_channel
8
+ subscribe :unsubscribe, :to => InternalController, :with_method => :unsubscribe_to_channel
8
9
  end
9
10
  end
10
11
  end
@@ -19,10 +20,16 @@ module WebsocketRails
19
20
  WebsocketRails[channel_name].subscribe connection
20
21
  trigger_success
21
22
  else
22
- trigger_failure( { :reason => "channel is private", :hint => "use subscibe_private instead." } )
23
+ trigger_failure( { :reason => "channel is private", :hint => "use subscribe_private instead." } )
23
24
  end
24
25
  end
25
26
 
27
+ def unsubscribe_to_channel
28
+ channel_name = event.data[:channel]
29
+ WebsocketRails[channel_name].unsubscribe connection
30
+ trigger_success
31
+ end
32
+
26
33
  def do_pong
27
34
  connection.pong = true
28
35
  end
@@ -1,3 +1,3 @@
1
1
  module WebsocketRails
2
- VERSION = "0.4.7"
2
+ VERSION = "0.4.8"
3
3
  end
@@ -153,3 +153,6 @@ Connecting to database specified by database.yml
153
153
  Connecting to database specified by database.yml
154
154
  Connecting to database specified by database.yml
155
155
  Connecting to database specified by database.yml
156
+ Connecting to database specified by database.yml
157
+ Connecting to database specified by database.yml
158
+ Connecting to database specified by database.yml
@@ -1,4 +1,3 @@
1
-
2
1
  /*
3
2
  The channel object is returned when you subscribe to a channel.
4
3
 
@@ -14,22 +13,17 @@ For instance:
14
13
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
15
14
 
16
15
  WebSocketRails.Channel = (function() {
17
-
18
16
  function Channel(name, _dispatcher, is_private) {
19
17
  var event, event_name;
20
18
  this.name = name;
21
19
  this._dispatcher = _dispatcher;
22
20
  this.is_private = is_private;
23
21
  this._failure_launcher = __bind(this._failure_launcher, this);
24
-
25
22
  this._success_launcher = __bind(this._success_launcher, this);
26
-
27
23
  this.dispatch = __bind(this.dispatch, this);
28
-
29
24
  this.trigger = __bind(this.trigger, this);
30
-
31
25
  this.bind = __bind(this.bind, this);
32
-
26
+ this.destroy = __bind(this.destroy, this);
33
27
  if (this.is_private) {
34
28
  event_name = 'websocket_rails.subscribe_private';
35
29
  } else {
@@ -46,9 +40,23 @@ For instance:
46
40
  this._callbacks = {};
47
41
  }
48
42
 
43
+ Channel.prototype.destroy = function() {
44
+ var event, event_name;
45
+ event_name = 'websocket_rails.unsubscribe';
46
+ event = new WebSocketRails.Event([
47
+ event_name, {
48
+ data: {
49
+ channel: this.name
50
+ }
51
+ }, this._dispatcher.connection_id
52
+ ]);
53
+ this._dispatcher.trigger_event(event);
54
+ return this._callbacks = {};
55
+ };
56
+
49
57
  Channel.prototype.bind = function(event_name, callback) {
50
- var _base, _ref;
51
- if ((_ref = (_base = this._callbacks)[event_name]) == null) {
58
+ var _base;
59
+ if ((_base = this._callbacks)[event_name] == null) {
52
60
  _base[event_name] = [];
53
61
  }
54
62
  return this._callbacks[event_name].push(callback);
@@ -1,4 +1,3 @@
1
-
2
1
  /*
3
2
  The Event object stores all the relevant event information.
4
3
  */
@@ -8,23 +7,16 @@ The Event object stores all the relevant event information.
8
7
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
9
8
 
10
9
  WebSocketRails.Event = (function() {
11
-
12
10
  function Event(data, success_callback, failure_callback) {
13
11
  var attr;
14
12
  this.success_callback = success_callback;
15
13
  this.failure_callback = failure_callback;
16
14
  this.run_callbacks = __bind(this.run_callbacks, this);
17
-
18
15
  this.attributes = __bind(this.attributes, this);
19
-
20
16
  this.serialize = __bind(this.serialize, this);
21
-
22
17
  this.is_ping = __bind(this.is_ping, this);
23
-
24
18
  this.is_result = __bind(this.is_result, this);
25
-
26
19
  this.is_channel = __bind(this.is_channel, this);
27
-
28
20
  this.name = data[0];
29
21
  attr = data[1];
30
22
  if (attr != null) {
@@ -1,4 +1,3 @@
1
-
2
1
  /*
3
2
  HTTP Interface for the WebSocketRails client.
4
3
  */
@@ -8,7 +7,6 @@
8
7
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
9
8
 
10
9
  WebSocketRails.HttpConnection = (function() {
11
-
12
10
  HttpConnection.prototype.httpFactories = function() {
13
11
  return [
14
12
  function() {
@@ -24,14 +22,15 @@
24
22
  };
25
23
 
26
24
  HttpConnection.prototype.createXMLHttpObject = function() {
27
- var factories, factory, xmlhttp, _i, _len;
25
+ var e, factories, factory, xmlhttp, _i, _len;
28
26
  xmlhttp = false;
29
27
  factories = this.httpFactories();
30
28
  for (_i = 0, _len = factories.length; _i < _len; _i++) {
31
29
  factory = factories[_i];
32
30
  try {
33
31
  xmlhttp = factory();
34
- } catch (e) {
32
+ } catch (_error) {
33
+ e = _error;
35
34
  continue;
36
35
  }
37
36
  break;
@@ -43,13 +42,9 @@
43
42
  this.url = url;
44
43
  this.dispatcher = dispatcher;
45
44
  this.flush_queue = __bind(this.flush_queue, this);
46
-
47
45
  this.trigger = __bind(this.trigger, this);
48
-
49
46
  this.parse_stream = __bind(this.parse_stream, this);
50
-
51
47
  this.createXMLHttpObject = __bind(this.createXMLHttpObject, this);
52
-
53
48
  this._url = this.url;
54
49
  this._conn = this.createXMLHttpObject();
55
50
  this.last_pos = 0;
@@ -1,4 +1,3 @@
1
-
2
1
  /*
3
2
  WebSocket Interface for the WebSocketRails client.
4
3
  */
@@ -8,20 +7,14 @@ WebSocket Interface for the WebSocketRails client.
8
7
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
9
8
 
10
9
  WebSocketRails.WebSocketConnection = (function() {
11
-
12
10
  function WebSocketConnection(url, dispatcher) {
13
11
  this.url = url;
14
12
  this.dispatcher = dispatcher;
15
13
  this.flush_queue = __bind(this.flush_queue, this);
16
-
17
14
  this.on_error = __bind(this.on_error, this);
18
-
19
15
  this.on_close = __bind(this.on_close, this);
20
-
21
16
  this.on_message = __bind(this.on_message, this);
22
-
23
17
  this.trigger = __bind(this.trigger, this);
24
-
25
18
  if (!this.url.match(/^wss?:\/\//)) {
26
19
  this.url = "ws://" + this.url;
27
20
  }
@@ -48,13 +41,13 @@ WebSocket Interface for the WebSocketRails client.
48
41
 
49
42
  WebSocketConnection.prototype.on_close = function(event) {
50
43
  var close_event;
51
- close_event = new WebSocketRails.Event(['connection_closed', {}]);
44
+ close_event = new WebSocketRails.Event(['connection_closed', event]);
52
45
  return this.dispatcher.dispatch(close_event);
53
46
  };
54
47
 
55
48
  WebSocketConnection.prototype.on_error = function(event) {
56
49
  var error_event;
57
- error_event = new WebSocketRails.Event(['connection_error', event != null ? event.data : void 0]);
50
+ error_event = new WebSocketRails.Event(['connection_error', event]);
58
51
  return this.dispatcher.dispatch(error_event);
59
52
  };
60
53
 
@@ -1,4 +1,3 @@
1
-
2
1
  /*
3
2
  WebsocketRails JavaScript Client
4
3
 
@@ -23,32 +22,21 @@ Listening for new events from the server
23
22
  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
24
23
 
25
24
  window.WebSocketRails = (function() {
26
-
27
25
  function WebSocketRails(url, use_websockets) {
28
26
  this.url = url;
29
27
  this.use_websockets = use_websockets != null ? use_websockets : true;
30
28
  this.pong = __bind(this.pong, this);
31
-
32
29
  this.supports_websockets = __bind(this.supports_websockets, this);
33
-
34
30
  this.dispatch_channel = __bind(this.dispatch_channel, this);
35
-
31
+ this.unsubscribe = __bind(this.unsubscribe, this);
36
32
  this.subscribe_private = __bind(this.subscribe_private, this);
37
-
38
33
  this.subscribe = __bind(this.subscribe, this);
39
-
40
34
  this.dispatch = __bind(this.dispatch, this);
41
-
42
35
  this.trigger_event = __bind(this.trigger_event, this);
43
-
44
36
  this.trigger = __bind(this.trigger, this);
45
-
46
37
  this.bind = __bind(this.bind, this);
47
-
48
38
  this.connection_established = __bind(this.connection_established, this);
49
-
50
39
  this.new_message = __bind(this.new_message, this);
51
-
52
40
  this.state = 'connecting';
53
41
  this.callbacks = {};
54
42
  this.channels = {};
@@ -98,8 +86,8 @@ Listening for new events from the server
98
86
  };
99
87
 
100
88
  WebSocketRails.prototype.bind = function(event_name, callback) {
101
- var _base, _ref;
102
- if ((_ref = (_base = this.callbacks)[event_name]) == null) {
89
+ var _base;
90
+ if ((_base = this.callbacks)[event_name] == null) {
103
91
  _base[event_name] = [];
104
92
  }
105
93
  return this.callbacks[event_name].push(callback);
@@ -113,8 +101,8 @@ Listening for new events from the server
113
101
  };
114
102
 
115
103
  WebSocketRails.prototype.trigger_event = function(event) {
116
- var _base, _name, _ref;
117
- if ((_ref = (_base = this.queue)[_name = event.id]) == null) {
104
+ var _base, _name;
105
+ if ((_base = this.queue)[_name = event.id] == null) {
118
106
  _base[_name] = event;
119
107
  }
120
108
  return this._conn.trigger(event);
@@ -156,6 +144,14 @@ Listening for new events from the server
156
144
  }
157
145
  };
158
146
 
147
+ WebSocketRails.prototype.unsubscribe = function(channel_name) {
148
+ if (this.channels[channel_name] == null) {
149
+ return;
150
+ }
151
+ this.channels[channel_name].destroy();
152
+ return delete this.channels[channel_name];
153
+ };
154
+
159
155
  WebSocketRails.prototype.dispatch_channel = function(event) {
160
156
  if (this.channels[event.channel] == null) {
161
157
  return;
@@ -1,5 +1,4 @@
1
1
  (function() {
2
-
3
2
  describe('WebSocketRails.Channel:', function() {
4
3
  beforeEach(function() {
5
4
  this.dispatcher = {
@@ -1,5 +1,4 @@
1
1
  (function() {
2
-
3
2
  describe('WebSocketRails.Event', function() {
4
3
  describe('standard events', function() {
5
4
  beforeEach(function() {
@@ -1,5 +1,4 @@
1
1
  (function() {
2
-
3
2
  describe('WebsocketRails.WebSocketConnection:', function() {
4
3
  beforeEach(function() {
5
4
  var dispatcher;
@@ -18,6 +17,7 @@
18
17
  return true;
19
18
  };
20
19
  };
20
+ this.dispatcher = dispatcher;
21
21
  return this.connection = new WebSocketRails.WebSocketConnection('localhost:3000/websocket', dispatcher);
22
22
  });
23
23
  describe('constructor', function() {
@@ -81,12 +81,31 @@
81
81
  });
82
82
  });
83
83
  describe('.on_close', function() {
84
- return it('should dispatch the connection_closed event', function() {
85
- var mock_dispatcher;
86
- mock_dispatcher = sinon.mock(this.connection.dispatcher);
87
- mock_dispatcher.expects('dispatch').once();
88
- this.connection.on_close();
89
- return mock_dispatcher.verify();
84
+ return it('should dispatch the connection_closed event and pass the original event', function() {
85
+ var close_event, dispatcher, event, lastCall;
86
+ event = new WebSocketRails.Event(['event', 'message']);
87
+ close_event = new WebSocketRails.Event(['connection_closed', event]);
88
+ sinon.spy(this.dispatcher, 'dispatch');
89
+ this.connection.on_close(event);
90
+ dispatcher = this.dispatcher.dispatch;
91
+ lastCall = dispatcher.lastCall.args[0];
92
+ expect(dispatcher.calledOnce).toBe(true);
93
+ expect(lastCall.data).toEqual(event.data);
94
+ return dispatcher.restore();
95
+ });
96
+ });
97
+ describe('.on_error', function() {
98
+ return it('should dispatch the connection_error event and pass the original event', function() {
99
+ var dispatcher, error_event, event, lastCall;
100
+ event = new WebSocketRails.Event(['event', 'message']);
101
+ error_event = new WebSocketRails.Event(['connection_error', event]);
102
+ sinon.spy(this.dispatcher, 'dispatch');
103
+ this.connection.on_error(event);
104
+ dispatcher = this.dispatcher.dispatch;
105
+ lastCall = dispatcher.lastCall.args[0];
106
+ expect(dispatcher.calledOnce).toBe(true);
107
+ expect(lastCall.data).toEqual(event.data);
108
+ return dispatcher.restore();
90
109
  });
91
110
  });
92
111
  return describe('.flush_queue', function() {