websocket-rails 0.1.8 → 0.1.9
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 +15 -2
- data/Gemfile +3 -1
- data/lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee +5 -0
- data/lib/websocket-rails.rb +31 -5
- data/lib/websocket_rails/channel.rb +17 -7
- data/lib/websocket_rails/channel_manager.rb +1 -1
- data/lib/websocket_rails/connection_adapters.rb +10 -9
- data/lib/websocket_rails/connection_adapters/http.rb +8 -8
- data/lib/websocket_rails/connection_adapters/web_socket.rb +4 -4
- data/lib/websocket_rails/connection_manager.rb +25 -12
- data/lib/websocket_rails/data_store.rb +10 -10
- data/lib/websocket_rails/dispatcher.rb +12 -9
- data/lib/websocket_rails/event.rb +23 -11
- data/lib/websocket_rails/event_map.rb +8 -8
- data/lib/websocket_rails/logging.rb +18 -2
- data/lib/websocket_rails/synchronization.rb +92 -0
- data/lib/websocket_rails/version.rb +1 -1
- data/spec/dummy/log/test.log +300 -0
- data/spec/integration/connection_manager_spec.rb +15 -15
- data/spec/javascripts/generated/assets/http_connection.js +1 -1
- data/spec/javascripts/generated/assets/websocket_connection.js +9 -0
- data/spec/unit/channel_spec.rb +3 -3
- data/spec/unit/connection_manager_spec.rb +25 -15
- data/spec/unit/dispatcher_spec.rb +21 -9
- data/spec/unit/event_spec.rb +11 -1
- data/spec/unit/logging_spec.rb +38 -0
- data/spec/unit/synchronization_spec.rb +68 -0
- metadata +56 -5
@@ -7,7 +7,7 @@ module WebsocketRails
|
|
7
7
|
class ProductController < BaseController
|
8
8
|
def update_list; true; end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def define_test_events
|
12
12
|
WebsocketRails.route_block = nil
|
13
13
|
WebsocketRails::EventMap.describe do
|
@@ -23,13 +23,13 @@ module WebsocketRails
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
27
|
-
before(:all)
|
26
|
+
|
27
|
+
before(:all) do
|
28
28
|
define_test_events
|
29
29
|
if defined?(ConnectionAdapters::Test)
|
30
30
|
ConnectionAdapters.adapters.delete( ConnectionAdapters::Test )
|
31
31
|
end
|
32
|
-
|
32
|
+
end
|
33
33
|
|
34
34
|
shared_examples "an evented rack server" do
|
35
35
|
context "new connections" do
|
@@ -38,12 +38,12 @@ module WebsocketRails
|
|
38
38
|
@server.call( env )
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
context "active connections" do
|
43
43
|
context "new message from client" do
|
44
44
|
let(:test_message) { ['change_username',{:user_name => 'Joe User'}] }
|
45
45
|
let(:encoded_message) { test_message.to_json }
|
46
|
-
|
46
|
+
|
47
47
|
it "should execute the controller action associated with the received event" do
|
48
48
|
ChatController.any_instance.should_receive(:change_username)
|
49
49
|
@server.call( env )
|
@@ -54,7 +54,7 @@ module WebsocketRails
|
|
54
54
|
context "new message from client under a namespace" do
|
55
55
|
let(:test_message) { ['products.update_list',{:product => 'x-ray-vision'}] }
|
56
56
|
let(:encoded_message) { test_message.to_json }
|
57
|
-
|
57
|
+
|
58
58
|
it "should execute the controller action under the correct namespace" do
|
59
59
|
ChatController.any_instance.should_not_receive(:update_user_list)
|
60
60
|
ProductController.any_instance.should_receive(:update_list)
|
@@ -64,17 +64,17 @@ module WebsocketRails
|
|
64
64
|
end
|
65
65
|
|
66
66
|
context "subscribing to a channel" do
|
67
|
-
let(:channel_message) { ['websocket_rails.subscribe',{:data => {
|
67
|
+
let(:channel_message) { ['websocket_rails.subscribe',{:data => {:channel => 'test_chan'}}] }
|
68
68
|
let(:encoded_channel_message) { channel_message.to_json }
|
69
69
|
|
70
70
|
it "should subscribe the connection to the correct channel" do
|
71
|
-
|
72
|
-
|
73
|
-
channel.should_receive(:subscribe).
|
74
|
-
socket.on_message encoded_channel_message
|
71
|
+
#channel = WebsocketRails[:test_chan]
|
72
|
+
#@server.call( env )
|
73
|
+
#channel.should_receive(:subscribe).with(socket)
|
74
|
+
#socket.on_message encoded_channel_message
|
75
75
|
end
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
context "client error" do
|
79
79
|
it "should execute the controller action associated with the 'client_error' event" do
|
80
80
|
ChatController.any_instance.should_receive(:error_occurred)
|
@@ -82,7 +82,7 @@ module WebsocketRails
|
|
82
82
|
socket.on_error
|
83
83
|
end
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
context "client disconnects" do
|
87
87
|
it "should execute the controller action associated with the 'client_disconnected' event" do
|
88
88
|
ChatController.any_instance.should_receive(:delete_user)
|
@@ -95,7 +95,7 @@ module WebsocketRails
|
|
95
95
|
|
96
96
|
context "WebSocket Adapter" do
|
97
97
|
let(:socket) { @server.connections.first }
|
98
|
-
|
98
|
+
|
99
99
|
before do
|
100
100
|
::Faye::WebSocket.stub(:websocket?).and_return(true)
|
101
101
|
@server = ConnectionManager.new
|
@@ -63,7 +63,7 @@
|
|
63
63
|
if (this._conn.readyState === 3) {
|
64
64
|
data = this._conn.responseText.substring(this.last_pos);
|
65
65
|
this.last_pos = this._conn.responseText.length;
|
66
|
-
data = data.replace(
|
66
|
+
data = data.replace(/\]\]\[\[/g, "],[");
|
67
67
|
decoded_data = JSON.parse(data);
|
68
68
|
return this.dispatcher.new_message(decoded_data);
|
69
69
|
}
|
@@ -14,6 +14,8 @@ WebSocket Interface for the WebSocketRails client.
|
|
14
14
|
this.dispatcher = dispatcher;
|
15
15
|
this.flush_queue = __bind(this.flush_queue, this);
|
16
16
|
|
17
|
+
this.on_error = __bind(this.on_error, this);
|
18
|
+
|
17
19
|
this.on_close = __bind(this.on_close, this);
|
18
20
|
|
19
21
|
this.on_message = __bind(this.on_message, this);
|
@@ -27,6 +29,7 @@ WebSocket Interface for the WebSocketRails client.
|
|
27
29
|
this._conn = new WebSocket(this.url);
|
28
30
|
this._conn.onmessage = this.on_message;
|
29
31
|
this._conn.onclose = this.on_close;
|
32
|
+
this._conn.onerror = this.on_error;
|
30
33
|
}
|
31
34
|
|
32
35
|
WebSocketConnection.prototype.trigger = function(event) {
|
@@ -49,6 +52,12 @@ WebSocket Interface for the WebSocketRails client.
|
|
49
52
|
return this.dispatcher.dispatch(close_event);
|
50
53
|
};
|
51
54
|
|
55
|
+
WebSocketConnection.prototype.on_error = function(event) {
|
56
|
+
var error_event;
|
57
|
+
error_event = new WebSocketRails.Event(['connection_error', event != null ? event.data : void 0]);
|
58
|
+
return this.dispatcher.dispatch(error_event);
|
59
|
+
};
|
60
|
+
|
52
61
|
WebSocketConnection.prototype.flush_queue = function() {
|
53
62
|
var event, _i, _len, _ref;
|
54
63
|
_ref = this.message_queue;
|
data/spec/unit/channel_spec.rb
CHANGED
@@ -24,14 +24,14 @@ module WebsocketRails
|
|
24
24
|
describe "#trigger" do
|
25
25
|
it "should create a new event and trigger it on all subscribers" do
|
26
26
|
event = double('event').as_null_object
|
27
|
-
Event.should_receive(:new) do |name,
|
27
|
+
Event.should_receive(:new) do |name,options|
|
28
28
|
name.should == 'event'
|
29
|
-
|
29
|
+
options[:data].should == 'data'
|
30
30
|
event
|
31
31
|
end
|
32
32
|
connection.should_receive(:trigger).with(event)
|
33
33
|
subject.subscribe connection
|
34
|
-
subject.trigger 'event',
|
34
|
+
subject.trigger 'event', 'data'
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
module WebsocketRails
|
4
4
|
describe ConnectionManager do
|
5
5
|
include Rack::Test::Methods
|
6
|
-
|
6
|
+
|
7
7
|
def app
|
8
8
|
@app ||= ConnectionManager.new
|
9
9
|
end
|
@@ -11,26 +11,36 @@ module WebsocketRails
|
|
11
11
|
def open_connection
|
12
12
|
subject.call(env)
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
let(:connections) { subject.connections }
|
16
16
|
let(:dispatcher) { subject.dispatcher }
|
17
|
-
|
17
|
+
|
18
18
|
before(:each) do
|
19
19
|
ConnectionAdapters::Base.any_instance.stub(:send)
|
20
20
|
@mock_socket = ConnectionAdapters::Base.new(mock_request,dispatcher)
|
21
21
|
ConnectionAdapters.stub(:establish_connection).and_return(@mock_socket)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
|
+
describe "#initialize" do
|
25
|
+
it "should create an empty connections array" do
|
26
|
+
subject.connections.should be_a Array
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should create a new dispatcher instance" do
|
30
|
+
subject.dispatcher.should be_a Dispatcher
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
24
34
|
context "new connections" do
|
25
35
|
it "should add one to the total connection count" do
|
26
36
|
expect { open_connection }.to change { connections.count }.by(1)
|
27
37
|
end
|
28
|
-
|
38
|
+
|
29
39
|
it "should store the new connection in the @connections array" do
|
30
40
|
open_connection
|
31
41
|
connections.include?(@mock_socket).should be_true
|
32
42
|
end
|
33
|
-
|
43
|
+
|
34
44
|
it "should return an Async Rack response" do
|
35
45
|
open_connection.should == [ -1, {}, [] ]
|
36
46
|
end
|
@@ -41,19 +51,19 @@ module WebsocketRails
|
|
41
51
|
@mock_http = ConnectionAdapters::Http.new(mock_request,dispatcher)
|
42
52
|
app.connections << @mock_http
|
43
53
|
end
|
44
|
-
|
54
|
+
|
45
55
|
it "should receive the new event for the correct connection" do
|
46
56
|
@mock_http.should_receive(:on_message).with(encoded_message)
|
47
57
|
post '/websocket', {:client_id => @mock_http.id, :data => encoded_message}
|
48
58
|
end
|
49
59
|
end
|
50
|
-
|
60
|
+
|
51
61
|
context "open connections" do
|
52
62
|
before(:each) do
|
53
63
|
ConnectionAdapters.stub(:establish_connection).and_return(@mock_socket,ConnectionAdapters::Base.new(mock_request,dispatcher))
|
54
64
|
4.times { open_connection }
|
55
65
|
end
|
56
|
-
|
66
|
+
|
57
67
|
context "when receiving a new event" do
|
58
68
|
before(:each) { open_connection }
|
59
69
|
|
@@ -67,17 +77,17 @@ module WebsocketRails
|
|
67
77
|
@mock_socket.on_message(mock_event)
|
68
78
|
end
|
69
79
|
end
|
70
|
-
|
80
|
+
|
71
81
|
context "when closing" do
|
72
82
|
it "should remove the connection object from the @connections array" do
|
73
83
|
@mock_socket.on_close
|
74
84
|
connections.include?(@mock_socket).should be_false
|
75
85
|
end
|
76
|
-
|
86
|
+
|
77
87
|
it "should decrement the connection count by one" do
|
78
88
|
expect { @mock_socket.on_close }.to change { connections.count }.by(-1)
|
79
89
|
end
|
80
|
-
|
90
|
+
|
81
91
|
it "should dispatch the :client_disconnected event" do
|
82
92
|
dispatcher.should_receive(:dispatch) do |event|
|
83
93
|
event.name.should == :client_disconnected
|
@@ -86,14 +96,14 @@ module WebsocketRails
|
|
86
96
|
@mock_socket.on_close
|
87
97
|
end
|
88
98
|
end
|
89
|
-
|
99
|
+
|
90
100
|
end
|
91
|
-
|
101
|
+
|
92
102
|
context "invalid connections" do
|
93
103
|
before(:each) do
|
94
104
|
ConnectionAdapters.stub(:establish_connection).and_raise(InvalidConnectionError)
|
95
105
|
end
|
96
|
-
|
106
|
+
|
97
107
|
it "should return a 400 bad request error code" do
|
98
108
|
open_connection.first.should == 400
|
99
109
|
end
|
@@ -2,22 +2,22 @@ require 'spec_helper'
|
|
2
2
|
require 'support/mock_web_socket'
|
3
3
|
|
4
4
|
module WebsocketRails
|
5
|
-
|
5
|
+
|
6
6
|
class EventTarget
|
7
7
|
attr_reader :_event, :test_method
|
8
|
-
|
8
|
+
|
9
9
|
def execute_observers(event_name)
|
10
10
|
true
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
describe Dispatcher do
|
15
|
-
|
15
|
+
|
16
16
|
let(:event) { double('Event') }
|
17
17
|
let(:connection) { MockWebSocket.new }
|
18
18
|
let(:connection_manager) { double('connection_manager').as_null_object }
|
19
19
|
subject { Dispatcher.new(connection_manager) }
|
20
|
-
|
20
|
+
|
21
21
|
describe "#receive_encoded" do
|
22
22
|
context "receiving a new message" do
|
23
23
|
before do
|
@@ -42,7 +42,7 @@ module WebsocketRails
|
|
42
42
|
subject.receive(:test_event,{},connection)
|
43
43
|
end
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
context "dispatching a message for an event" do
|
47
47
|
before do
|
48
48
|
@target = EventTarget.new
|
@@ -51,13 +51,14 @@ module WebsocketRails
|
|
51
51
|
event.stub(:data).and_return(:some_message)
|
52
52
|
event.stub(:connection).and_return(connection)
|
53
53
|
event.stub(:is_channel?).and_return(false)
|
54
|
+
event.stub(:is_invalid?).and_return(false)
|
54
55
|
end
|
55
|
-
|
56
|
+
|
56
57
|
it "should execute the correct method on the target class" do
|
57
58
|
@target.should_receive(:test_method)
|
58
59
|
subject.dispatch(event)
|
59
60
|
end
|
60
|
-
|
61
|
+
|
61
62
|
it "should set the _event instance variable on the target object" do
|
62
63
|
subject.dispatch(event)
|
63
64
|
@target._event.should == event
|
@@ -72,8 +73,19 @@ module WebsocketRails
|
|
72
73
|
subject.dispatch event
|
73
74
|
end
|
74
75
|
end
|
76
|
+
|
77
|
+
context "invalid events" do
|
78
|
+
before do
|
79
|
+
event.stub(:is_invalid?).and_return(true)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should not dispatch the event" do
|
83
|
+
subject.should_not_receive(:route)
|
84
|
+
subject.dispatch(event)
|
85
|
+
end
|
86
|
+
end
|
75
87
|
end
|
76
|
-
|
88
|
+
|
77
89
|
describe "#send_message" do
|
78
90
|
before do
|
79
91
|
@event = Event.new_from_json( encoded_message, connection )
|
data/spec/unit/event_spec.rb
CHANGED
@@ -6,7 +6,8 @@ module WebsocketRails
|
|
6
6
|
let(:encoded_message_string) { '["new_message",{"id":"1234","data":"this is a message"}]' }
|
7
7
|
let(:namespace_encoded_message_string) { '["product.new_message",{"id":"1234","data":"this is a message"}]' }
|
8
8
|
let(:namespace_encoded_message) { '["product.new_message",{"id":"1234","data":{"message":"this is a message"}}]' }
|
9
|
-
let(:channel_encoded_message_string) { '["new_message",{"id":"1234","channel":"awesome_channel","data":"this is a message","success":null,"result":null}]' }
|
9
|
+
let(:channel_encoded_message_string) { '["new_message",{"id":"1234","channel":"awesome_channel","data":"this is a message","success":null,"result":null,"server_token":"1234"}]' }
|
10
|
+
let(:synchronizable_encoded_message) { '["new_message",{"id":"1234","data":{"message":"this is a message"},"server_token":"1234"}]' }
|
10
11
|
let(:connection) { double('connection') }
|
11
12
|
|
12
13
|
before { connection.stub!(:id).and_return(1) }
|
@@ -120,6 +121,15 @@ module WebsocketRails
|
|
120
121
|
event.serialize.should == channel_encoded_message_string
|
121
122
|
end
|
122
123
|
end
|
124
|
+
|
125
|
+
context "messages for synchronization" do
|
126
|
+
it "should include the unique server token" do
|
127
|
+
event = Event.new_from_json synchronizable_encoded_message, connection
|
128
|
+
raw_data = event.serialize
|
129
|
+
data = JSON.parse raw_data
|
130
|
+
data[1]['server_token'].should == '1234'
|
131
|
+
end
|
132
|
+
end
|
123
133
|
end
|
124
134
|
|
125
135
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module WebsocketRails
|
4
|
+
class ClassWithLogging
|
5
|
+
include Logging
|
6
|
+
end
|
7
|
+
|
8
|
+
describe ClassWithLogging do
|
9
|
+
|
10
|
+
describe "#log" do
|
11
|
+
context "when log_level = :warn" do
|
12
|
+
before do
|
13
|
+
WebsocketRails.setup do |config|
|
14
|
+
config.log_level = :warn
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should not print to the console" do
|
19
|
+
subject.should_not_receive(:puts).with("test message")
|
20
|
+
subject.log "test message"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "log_level = :debug" do
|
25
|
+
before do
|
26
|
+
WebsocketRails.setup do |config|
|
27
|
+
config.log_level = :debug
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should print to the console if log_level is :debug" do
|
32
|
+
subject.should_receive(:puts).with("test message")
|
33
|
+
subject.log "test message"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "eventmachine"
|
3
|
+
|
4
|
+
module WebsocketRails
|
5
|
+
describe Synchronization do
|
6
|
+
|
7
|
+
around(:each) do |example|
|
8
|
+
EM.run do
|
9
|
+
Fiber.new do
|
10
|
+
@redis = Redis.new
|
11
|
+
@redis.del "websocket_rails.active_servers"
|
12
|
+
example.run
|
13
|
+
end.resume
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
after(:each) do
|
18
|
+
EM.stop
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:subject) { Synchronization }
|
22
|
+
|
23
|
+
describe "#publish" do
|
24
|
+
it "should add the serialized event to the websocket_rails.events channel" do
|
25
|
+
event = Event.new(:test_event, :channel => 'synchrony', :data => 'hello channel')
|
26
|
+
Redis.any_instance.should_receive(:publish).with("websocket_rails.events", event.serialize)
|
27
|
+
|
28
|
+
subject.publish(event)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#generate_unique_token" do
|
33
|
+
before do
|
34
|
+
SecureRandom.stub(:urlsafe_base64).and_return(1, 2, 3)
|
35
|
+
end
|
36
|
+
|
37
|
+
after do
|
38
|
+
@redis.del "websocket_rails.active_servers"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should generate a unique token" do
|
42
|
+
SecureRandom.should_receive(:urlsafe_base64).at_least(1).times
|
43
|
+
subject.generate_unique_token
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should generate another id if the current id is already registered" do
|
47
|
+
@redis.sadd "websocket_rails.active_servers", 1
|
48
|
+
token = subject.generate_unique_token
|
49
|
+
token.should == 2
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#register_server" do
|
54
|
+
it "should add the unique token to the active_servers key in redis" do
|
55
|
+
Redis.any_instance.should_receive(:sadd).with("websocket_rails.active_servers", "token")
|
56
|
+
subject.register_server "token"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#remove_server" do
|
61
|
+
it "should add the unique token to the active_servers key in redis" do
|
62
|
+
Redis.any_instance.should_receive(:srem).with("websocket_rails.active_servers", "token")
|
63
|
+
subject.remove_server "token"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|