wwl-websocket-rails 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +328 -0
- data/Gemfile +27 -0
- data/MIT-LICENSE +20 -0
- data/README.md +239 -0
- data/Rakefile +72 -0
- data/bin/thin-socketrails +45 -0
- data/lib/assets/javascripts/websocket_rails/abstract_connection.js.coffee +45 -0
- data/lib/assets/javascripts/websocket_rails/channel.js.coffee +70 -0
- data/lib/assets/javascripts/websocket_rails/event.js.coffee +46 -0
- data/lib/assets/javascripts/websocket_rails/http_connection.js.coffee +66 -0
- data/lib/assets/javascripts/websocket_rails/main.js +6 -0
- data/lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee +29 -0
- data/lib/assets/javascripts/websocket_rails/websocket_rails.js.coffee +158 -0
- data/lib/config.ru +3 -0
- data/lib/generators/websocket_rails/install/install_generator.rb +33 -0
- data/lib/generators/websocket_rails/install/templates/events.rb +14 -0
- data/lib/generators/websocket_rails/install/templates/websocket_rails.rb +68 -0
- data/lib/rails/app/controllers/websocket_rails/delegation_controller.rb +13 -0
- data/lib/rails/config/routes.rb +7 -0
- data/lib/rails/tasks/websocket_rails.tasks +42 -0
- data/lib/spec_helpers/matchers/route_matchers.rb +65 -0
- data/lib/spec_helpers/matchers/trigger_matchers.rb +138 -0
- data/lib/spec_helpers/spec_helper_event.rb +34 -0
- data/lib/websocket-rails.rb +108 -0
- data/lib/websocket_rails/base_controller.rb +208 -0
- data/lib/websocket_rails/channel.rb +97 -0
- data/lib/websocket_rails/channel_manager.rb +55 -0
- data/lib/websocket_rails/configuration.rb +177 -0
- data/lib/websocket_rails/connection_adapters/http.rb +120 -0
- data/lib/websocket_rails/connection_adapters/web_socket.rb +35 -0
- data/lib/websocket_rails/connection_adapters.rb +195 -0
- data/lib/websocket_rails/connection_manager.rb +119 -0
- data/lib/websocket_rails/controller_factory.rb +80 -0
- data/lib/websocket_rails/data_store.rb +145 -0
- data/lib/websocket_rails/dispatcher.rb +129 -0
- data/lib/websocket_rails/engine.rb +26 -0
- data/lib/websocket_rails/event.rb +193 -0
- data/lib/websocket_rails/event_map.rb +184 -0
- data/lib/websocket_rails/event_queue.rb +33 -0
- data/lib/websocket_rails/internal_events.rb +37 -0
- data/lib/websocket_rails/logging.rb +133 -0
- data/lib/websocket_rails/spec_helpers.rb +3 -0
- data/lib/websocket_rails/synchronization.rb +178 -0
- data/lib/websocket_rails/user_manager.rb +276 -0
- data/lib/websocket_rails/version.rb +3 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/chat_controller.rb +53 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +45 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +22 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +26 -0
- data/spec/dummy/config/environments/production.rb +49 -0
- data/spec/dummy/config/environments/test.rb +34 -0
- data/spec/dummy/config/events.rb +7 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20130902222552_create_users.rb +10 -0
- data/spec/dummy/db/schema.rb +23 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +17 -0
- data/spec/dummy/log/production.log +0 -0
- data/spec/dummy/log/server.log +0 -0
- data/spec/dummy/log/test.log +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/javascripts/application.js +2 -0
- data/spec/dummy/public/javascripts/controls.js +965 -0
- data/spec/dummy/public/javascripts/dragdrop.js +974 -0
- data/spec/dummy/public/javascripts/effects.js +1123 -0
- data/spec/dummy/public/javascripts/prototype.js +6001 -0
- data/spec/dummy/public/javascripts/rails.js +202 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/integration/connection_manager_spec.rb +135 -0
- data/spec/javascripts/support/jasmine.yml +52 -0
- data/spec/javascripts/support/jasmine_helper.rb +38 -0
- data/spec/javascripts/support/vendor/sinon-1.7.1.js +4343 -0
- data/spec/javascripts/websocket_rails/channel_spec.coffee +112 -0
- data/spec/javascripts/websocket_rails/event_spec.coffee +81 -0
- data/spec/javascripts/websocket_rails/helpers.coffee +6 -0
- data/spec/javascripts/websocket_rails/websocket_connection_spec.coffee +158 -0
- data/spec/javascripts/websocket_rails/websocket_rails_spec.coffee +273 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/spec_helpers/matchers/route_matchers_spec.rb +109 -0
- data/spec/spec_helpers/matchers/trigger_matchers_spec.rb +358 -0
- data/spec/spec_helpers/spec_helper_event_spec.rb +66 -0
- data/spec/support/helper_methods.rb +42 -0
- data/spec/support/mock_web_socket.rb +41 -0
- data/spec/unit/base_controller_spec.rb +74 -0
- data/spec/unit/channel_manager_spec.rb +58 -0
- data/spec/unit/channel_spec.rb +169 -0
- data/spec/unit/connection_adapters/http_spec.rb +88 -0
- data/spec/unit/connection_adapters/web_socket_spec.rb +30 -0
- data/spec/unit/connection_adapters_spec.rb +259 -0
- data/spec/unit/connection_manager_spec.rb +148 -0
- data/spec/unit/controller_factory_spec.rb +76 -0
- data/spec/unit/data_store_spec.rb +106 -0
- data/spec/unit/dispatcher_spec.rb +203 -0
- data/spec/unit/event_map_spec.rb +120 -0
- data/spec/unit/event_queue_spec.rb +36 -0
- data/spec/unit/event_spec.rb +181 -0
- data/spec/unit/logging_spec.rb +162 -0
- data/spec/unit/synchronization_spec.rb +150 -0
- data/spec/unit/target_validator_spec.rb +88 -0
- data/spec/unit/user_manager_spec.rb +165 -0
- metadata +320 -0
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module WebsocketRails
|
4
|
+
describe ConnectionManager do
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
def app
|
8
|
+
@app ||= ConnectionManager.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def open_connection
|
12
|
+
subject.call(env)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:connections) { subject.connections }
|
16
|
+
let(:dispatcher) { subject.dispatcher }
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
ConnectionAdapters::Base.any_instance.stub(:send)
|
20
|
+
@mock_socket = ConnectionAdapters::Base.new(mock_request, dispatcher)
|
21
|
+
ConnectionAdapters.stub(:establish_connection).and_return(@mock_socket)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#initialize" do
|
25
|
+
it "should create an empty connections hash" do
|
26
|
+
subject.connections.should be_a Hash
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should create a new dispatcher instance" do
|
30
|
+
subject.dispatcher.should be_a Dispatcher
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "new connections" do
|
35
|
+
it "should add one to the total connection count" do
|
36
|
+
expect { open_connection }.to change { connections.count }.by(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should store the new connection in the @connections Hash" do
|
40
|
+
open_connection
|
41
|
+
connections[@mock_socket.id].should == @mock_socket
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should return an Async Rack response" do
|
45
|
+
open_connection.should == [ -1, {}, [] ]
|
46
|
+
end
|
47
|
+
|
48
|
+
before do
|
49
|
+
SecureRandom.stub(:hex).and_return(1, 2)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "gives the new connection a unique ID" do
|
53
|
+
@mock_socket.should_receive(:id=).with(1)
|
54
|
+
open_connection
|
55
|
+
@mock_socket.should_receive(:id=).with(2)
|
56
|
+
open_connection
|
57
|
+
end
|
58
|
+
|
59
|
+
context "user connections" do
|
60
|
+
before do
|
61
|
+
@mock_socket.stub(:user_connection?).and_return true
|
62
|
+
@mock_socket.stub(:user_identifier).and_return "El Jefe"
|
63
|
+
open_connection
|
64
|
+
end
|
65
|
+
|
66
|
+
it "stores the connection in the UserManager" do
|
67
|
+
WebsocketRails.users["El Jefe"].connections.first.should == @mock_socket
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "new POST event" do
|
73
|
+
before(:each) do
|
74
|
+
@mock_http = ConnectionAdapters::Http.new(mock_request, dispatcher)
|
75
|
+
@mock_http.id = 'is_i_as_string'
|
76
|
+
app.connections[@mock_http.id] = @mock_http
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should receive the new event for the correct connection" do
|
80
|
+
@mock_http.should_receive(:on_message).with(encoded_message)
|
81
|
+
post '/websocket', {:client_id => @mock_http.id, :data => encoded_message}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "open connections" do
|
86
|
+
before(:each) do
|
87
|
+
ConnectionAdapters.stub(:establish_connection).and_return(@mock_socket, ConnectionAdapters::Base.new(mock_request, dispatcher))
|
88
|
+
4.times { open_connection }
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when receiving a new event" do
|
92
|
+
before(:each) { open_connection }
|
93
|
+
|
94
|
+
it "should dispatch the appropriate event through the Dispatcher" do
|
95
|
+
mock_event = ["new_message",{:data =>"data"}].to_json
|
96
|
+
dispatcher.should_receive(:dispatch) do |event|
|
97
|
+
event.name.should == :new_message
|
98
|
+
event.data.should == "data"
|
99
|
+
event.connection.should == @mock_socket
|
100
|
+
end
|
101
|
+
@mock_socket.on_message(mock_event)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when closing" do
|
106
|
+
it "should remove the connection object from the @connections array" do
|
107
|
+
@mock_socket.on_close
|
108
|
+
connections.has_key?(@mock_socket.id).should be_false
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should decrement the connection count by one" do
|
112
|
+
expect { @mock_socket.on_close }.to change { connections.count }.by(-1)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should dispatch the :client_disconnected event" do
|
116
|
+
dispatcher.should_receive(:dispatch) do |event|
|
117
|
+
event.name.should == :client_disconnected
|
118
|
+
event.connection.should == @mock_socket
|
119
|
+
end
|
120
|
+
@mock_socket.on_close
|
121
|
+
end
|
122
|
+
|
123
|
+
context "user connections" do
|
124
|
+
before do
|
125
|
+
@mock_socket.stub(:user_connection?).and_return true
|
126
|
+
@mock_socket.stub(:user_identifier).and_return "El Jefe"
|
127
|
+
end
|
128
|
+
|
129
|
+
it "deletes the connection from the UserManager" do
|
130
|
+
@mock_socket.on_close
|
131
|
+
WebsocketRails.users["El Jefe"].class.should == UserManager::MissingConnection
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
context "invalid connections" do
|
139
|
+
before(:each) do
|
140
|
+
ConnectionAdapters.stub(:establish_connection).and_raise(InvalidConnectionError)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should return a 400 bad request error code" do
|
144
|
+
open_connection.first.should == 400
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module WebsocketRails
|
4
|
+
describe ControllerFactory do
|
5
|
+
|
6
|
+
class TestController < BaseController
|
7
|
+
attr_reader :_dispatcher, :_event
|
8
|
+
|
9
|
+
def initialize_session
|
10
|
+
true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:dispatcher) { double('dispatcher') }
|
15
|
+
let(:connection) { double('connection') }
|
16
|
+
let(:event) { double('event') }
|
17
|
+
|
18
|
+
subject { ControllerFactory.new(dispatcher) }
|
19
|
+
|
20
|
+
before do
|
21
|
+
connection.stub(:id).and_return(1)
|
22
|
+
event.stub(:connection).and_return(connection)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "stores a reference to the dispatcher" do
|
26
|
+
subject.dispatcher.should == dispatcher
|
27
|
+
end
|
28
|
+
|
29
|
+
it "maintains a hash of controller data stores" do
|
30
|
+
subject.controller_stores.should be_a Hash
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#new_for_event" do
|
34
|
+
|
35
|
+
context "when Rails is defined and env is set to development" do
|
36
|
+
|
37
|
+
it "creates and returns a controller instance of the InternalController" do
|
38
|
+
rails_env = double(:rails_env)
|
39
|
+
Rails.stub(:env).and_return rails_env
|
40
|
+
rails_env.stub(:development?).and_return true
|
41
|
+
controller = subject.new_for_event(event, InternalController, 'some_method')
|
42
|
+
controller.class.should == InternalController
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
it "creates and returns a new controller instance" do
|
48
|
+
controller = subject.new_for_event(event, TestController, 'some_method')
|
49
|
+
controller.class.should == TestController
|
50
|
+
end
|
51
|
+
|
52
|
+
it "initializes the controller with the correct data_store" do
|
53
|
+
store = double('data_store')
|
54
|
+
subject.controller_stores[TestController] = store
|
55
|
+
controller = subject.new_for_event(event, TestController, 'some_method')
|
56
|
+
controller.controller_store.should == store
|
57
|
+
end
|
58
|
+
|
59
|
+
it "initializes the controller with the correct event" do
|
60
|
+
controller = subject.new_for_event(event, TestController, 'some_method')
|
61
|
+
controller.event.should == event
|
62
|
+
end
|
63
|
+
|
64
|
+
it "initializes the controller with the correct dispatcher" do
|
65
|
+
controller = subject.new_for_event(event, TestController, 'some_method')
|
66
|
+
controller._dispatcher.should == dispatcher
|
67
|
+
end
|
68
|
+
|
69
|
+
it "calls #initialize_session on the controller only once" do
|
70
|
+
TestController.any_instance.should_receive(:initialize_session).once
|
71
|
+
3.times { subject.new_for_event(event, TestController, 'some_method') }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module WebsocketRails
|
4
|
+
module DataStore
|
5
|
+
describe Base do
|
6
|
+
it "extends Hash" do
|
7
|
+
subject.should be_a Hash
|
8
|
+
end
|
9
|
+
|
10
|
+
it "allows indifferent access" do
|
11
|
+
subject['key'] = true
|
12
|
+
subject[:key].should == true
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#instances" do
|
16
|
+
before do
|
17
|
+
Base.clear_all_instances
|
18
|
+
@connection = double('connection')
|
19
|
+
@controller = double('controller')
|
20
|
+
end
|
21
|
+
|
22
|
+
it "keeps track of all instantiated instances" do
|
23
|
+
store_one = Base.new
|
24
|
+
store_two = Base.new
|
25
|
+
|
26
|
+
store_one.instances.count.should == 2
|
27
|
+
store_two.instances.count.should == 2
|
28
|
+
end
|
29
|
+
|
30
|
+
it "separates instances based on class name" do
|
31
|
+
2.times { Connection.new(@connection) }
|
32
|
+
4.times { Controller.new(@controller) }
|
33
|
+
|
34
|
+
Connection.new(@connection).instances.count.should == 3
|
35
|
+
Controller.new(@controller).instances.count.should == 5
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#destroy!" do
|
40
|
+
before do
|
41
|
+
Base.clear_all_instances
|
42
|
+
@store = Base.new
|
43
|
+
@other = Base.new
|
44
|
+
end
|
45
|
+
|
46
|
+
it "removes itself from the instances collection" do
|
47
|
+
@other.instances.count.should == 2
|
48
|
+
@store.destroy!
|
49
|
+
@other.instances.count.should == 1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#collect_all" do
|
54
|
+
before do
|
55
|
+
Base.clear_all_instances
|
56
|
+
@store_one = Base.new
|
57
|
+
@store_two = Base.new
|
58
|
+
|
59
|
+
@store_one[:secret] = 'token_one'
|
60
|
+
@store_two[:secret] = 'token_two'
|
61
|
+
end
|
62
|
+
|
63
|
+
context "called without a block" do
|
64
|
+
it "returns an array of values for the specified key from all store instances" do
|
65
|
+
secrets = @store_one.collect_all(:secret)
|
66
|
+
secrets.should == ['token_one', 'token_two']
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "called with a block" do
|
71
|
+
it "yields each value to the block" do
|
72
|
+
@store_one.collect_all(:secret) do |item|
|
73
|
+
item.should be_in ['token_one', 'token_two']
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe Connection do
|
81
|
+
before do
|
82
|
+
@connection = double('connection')
|
83
|
+
@connection.stub(:client_id).and_return(1)
|
84
|
+
end
|
85
|
+
|
86
|
+
let(:subject) { DataStore::Connection.new(@connection) }
|
87
|
+
|
88
|
+
it "stores a reference to it's connection" do
|
89
|
+
subject.connection.should == @connection
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe Controller do
|
94
|
+
before do
|
95
|
+
@controller = double('controller')
|
96
|
+
end
|
97
|
+
|
98
|
+
let(:subject) { DataStore::Controller.new(@controller) }
|
99
|
+
|
100
|
+
it "stores a reference to it's controller" do
|
101
|
+
subject.controller.should == @controller
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/mock_web_socket'
|
3
|
+
|
4
|
+
def rename_module_const(mod, old_name, new_name)
|
5
|
+
if mod.const_defined? old_name
|
6
|
+
mod.const_set(new_name, mod.const_get(old_name))
|
7
|
+
mod.send(:remove_const, old_name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def swizzle_module_const(mod, name, temp_name, &block)
|
12
|
+
rename_module_const(mod, name, temp_name)
|
13
|
+
yield block
|
14
|
+
rename_module_const(mod, temp_name, name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_temp_module_const(mod, name, value, &block)
|
18
|
+
mod.const_set(name, value)
|
19
|
+
yield block
|
20
|
+
mod.send(:remove_const, name)
|
21
|
+
end
|
22
|
+
|
23
|
+
module WebsocketRails
|
24
|
+
|
25
|
+
class EventTarget
|
26
|
+
attr_reader :_event, :_dispatcher, :test_method, :catch_all_method
|
27
|
+
end
|
28
|
+
|
29
|
+
describe Dispatcher do
|
30
|
+
|
31
|
+
let(:event) { double('Event').as_null_object }
|
32
|
+
let(:connection) { MockWebSocket.new }
|
33
|
+
let(:connection_manager) { double('connection_manager').as_null_object }
|
34
|
+
subject { Dispatcher.new(connection_manager) }
|
35
|
+
|
36
|
+
describe "#receive_encoded" do
|
37
|
+
context "receiving a new message" do
|
38
|
+
before do
|
39
|
+
Event.stub(:new_from_json).and_return( event )
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be decoded from JSON and dispatched" do
|
43
|
+
subject.stub(:dispatch) do |dispatch_event|
|
44
|
+
dispatch_event.should == event
|
45
|
+
end
|
46
|
+
subject.receive_encoded(encoded_message,connection)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#receive" do
|
52
|
+
before { Event.stub(:new).and_return( event ) }
|
53
|
+
|
54
|
+
it "should dispatch a new event" do
|
55
|
+
subject.stub(:dispatch) do |dispatch_event|
|
56
|
+
dispatch_event.should == event
|
57
|
+
end
|
58
|
+
subject.receive(:test_event,{},connection)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "dispatching an event" do
|
63
|
+
before do
|
64
|
+
EventMap.any_instance.stub(:routes_for).with(any_args).and_yield(EventTarget, :test_method)
|
65
|
+
event.stub(:name).and_return(:test_method)
|
66
|
+
event.stub(:data).and_return(:some_message)
|
67
|
+
event.stub(:connection).and_return(connection)
|
68
|
+
event.stub(:is_channel?).and_return(false)
|
69
|
+
event.stub(:is_invalid?).and_return(false)
|
70
|
+
event.stub(:is_internal?).and_return(false)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should execute the correct method on the target class" do
|
74
|
+
EventTarget.any_instance.should_receive(:process_action).with(:test_method, event)
|
75
|
+
subject.dispatch(event)
|
76
|
+
end
|
77
|
+
|
78
|
+
context "channel events" do
|
79
|
+
before do
|
80
|
+
subject.stub(:filtered_channels).and_return({})
|
81
|
+
end
|
82
|
+
it "should forward the data to the correct channel" do
|
83
|
+
event = Event.new 'test', :data => 'data', :channel => :awesome_channel
|
84
|
+
channel = double('channel')
|
85
|
+
channel.should_receive(:trigger_event).with(event)
|
86
|
+
WebsocketRails.should_receive(:[]).with(:awesome_channel).and_return(channel)
|
87
|
+
subject.dispatch event
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "filtered channel events" do
|
92
|
+
before do
|
93
|
+
subject.stub(:filtered_channels).and_return({:awesome_channel => EventTarget})
|
94
|
+
end
|
95
|
+
it "should execute the correct method on the target class" do
|
96
|
+
event = Event.new 'test_method', :data => 'some data', :channel => :awesome_channel
|
97
|
+
EventTarget.any_instance.should_receive(:process_action).with(:test_method, event)
|
98
|
+
subject.dispatch(event)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "filtered channel catch all events" do
|
103
|
+
before do
|
104
|
+
subject.stub(:filtered_channels).and_return({:awesome_channel => [EventTarget, :catch_all_method]})
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should execute the correct method on the target class" do
|
108
|
+
event = Event.new 'test_method', :data => 'some data', :channel => :awesome_channel
|
109
|
+
EventTarget.any_instance.should_receive(:process_action).with(:test_method, event)
|
110
|
+
EventTarget.any_instance.should_receive(:process_action).with(:catch_all_method, event)
|
111
|
+
subject.dispatch(event)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "invalid events" do
|
116
|
+
before do
|
117
|
+
event.stub(:is_invalid?).and_return(true)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should not dispatch the event" do
|
121
|
+
subject.should_not_receive(:route)
|
122
|
+
subject.dispatch(event)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "#send_message" do
|
128
|
+
before do
|
129
|
+
@event = Event.new_from_json( encoded_message, connection )
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should send a message to the event's connection object" do
|
133
|
+
connection.should_receive(:trigger).with(@event)
|
134
|
+
subject.send_message @event
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "#broadcast_message" do
|
139
|
+
before do
|
140
|
+
connection_manager.stub(:connections).and_return({"connection_id" => connection})
|
141
|
+
@event = Event.new_from_json( encoded_message, connection )
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should send a message to all connected clients" do
|
145
|
+
connection.should_receive(:trigger).with(@event)
|
146
|
+
subject.broadcast_message @event
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe 'record_invalid_defined?' do
|
151
|
+
|
152
|
+
it 'should return false when RecordInvalid is not defined' do
|
153
|
+
if Object.const_defined?('ActiveRecord')
|
154
|
+
swizzle_module_const(ActiveRecord, 'RecordInvalid','TempRecordInvalid') do
|
155
|
+
subject.send(:record_invalid_defined?).should be_false
|
156
|
+
end
|
157
|
+
else
|
158
|
+
set_temp_module_const(Object, 'ActiveRecord', Module.new) do
|
159
|
+
subject.send(:record_invalid_defined?).should be_false
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should return false when ActiveRecord is not defined' do
|
165
|
+
swizzle_module_const(Object, 'ActiveRecord', 'TempActiveRecord') do
|
166
|
+
subject.send(:record_invalid_defined?).should be_false
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'should return true if ActiveRecord::RecordInvalid is defined' do
|
171
|
+
if Object.const_defined?('ActiveRecord')
|
172
|
+
if ActiveRecord.const_defined?('RecordInvalid')
|
173
|
+
subject.send(:record_invalid_defined?).should be_true
|
174
|
+
else
|
175
|
+
set_temp_module_const(ActiveRecord, 'RecordInvalid', Class.new) do
|
176
|
+
subject.send(:record_invalid_defined?).should be_true
|
177
|
+
end
|
178
|
+
end
|
179
|
+
else
|
180
|
+
set_temp_module_const(Object, 'ActiveRecord', Module.new) do
|
181
|
+
set_temp_module_const(ActiveRecord, 'RecordInvalid', Class.new) do
|
182
|
+
subject.send(:record_invalid_defined?).should be_true
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'when ActiveRecord::RecordInvalid is not defined' do
|
190
|
+
|
191
|
+
it 'should check that exception can be converted to JSON' do
|
192
|
+
subject.should_receive(:record_invalid_defined?).and_return false
|
193
|
+
ex = double(:exception)
|
194
|
+
ex.should_receive(:respond_to?).with(:to_json).and_return true
|
195
|
+
exception_data = subject.send(:extract_exception_data, ex)
|
196
|
+
exception_data.should == ex
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# this classes are outside the WebsocketRails namespace to better reflect the actual
|
4
|
+
# situation in a normal usage
|
5
|
+
|
6
|
+
class ProductController < WebsocketRails::BaseController
|
7
|
+
def update_product
|
8
|
+
true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# name is chosen so that we know camelize is working correctly
|
13
|
+
class ComplexProductController < WebsocketRails::BaseController
|
14
|
+
|
15
|
+
def simplify_product
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
module WebsocketRails
|
23
|
+
describe EventMap do
|
24
|
+
|
25
|
+
|
26
|
+
def define_test_events
|
27
|
+
WebsocketRails.config.route_block = nil
|
28
|
+
WebsocketRails::EventMap.describe do
|
29
|
+
subscribe :client_connected, :to => ChatController, :with_method => :new_user
|
30
|
+
|
31
|
+
namespace :product do
|
32
|
+
subscribe :update, :to => ProductController, :with_method => :update_product
|
33
|
+
end
|
34
|
+
|
35
|
+
namespace :complex_product do
|
36
|
+
subscribe :simplify, 'complex_product#simplify'
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:dispatcher) { double('dispatcher').as_null_object }
|
43
|
+
subject { EventMap.new(dispatcher) }
|
44
|
+
before { define_test_events }
|
45
|
+
|
46
|
+
context "EventMap.describe" do
|
47
|
+
it "should store the event route block in the global configuration" do
|
48
|
+
WebsocketRails.config.route_block.should be_present
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "Events in the global namespace" do
|
53
|
+
|
54
|
+
it "should store the event in the correct namespace" do
|
55
|
+
subject.namespace.actions[:client_connected].should be_present
|
56
|
+
subject.namespace.name.should == :global
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should store the class constant and method name in the events hash" do
|
60
|
+
subject.namespace.actions[:client_connected].should == [[ChatController,:new_user]]
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
context "Events in a child namespace" do
|
66
|
+
|
67
|
+
before { @namespace = subject.namespace }
|
68
|
+
|
69
|
+
it "should store the event in the correct namespaces" do
|
70
|
+
@namespace.namespaces[:product].actions[:update].should be_present
|
71
|
+
@namespace.namespaces[:complex_product].actions[:simplify].should be_present
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
context "#routes_for" do
|
77
|
+
context "with events in the global namespace" do
|
78
|
+
it "should yield the controller class and action name for each route defined for an event" do
|
79
|
+
event = HelperMethods::MockEvent.new(:client_connected, [:global])
|
80
|
+
|
81
|
+
subject.routes_for(event) do |klass, method|
|
82
|
+
klass.should == ChatController
|
83
|
+
method.should == :new_user
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "with events in a child namespace" do
|
89
|
+
it "should yield the controller and action name for each route defined with a hash for an event" do
|
90
|
+
ProductController.any_instance.should_receive(:action_executed)
|
91
|
+
event = HelperMethods::MockEvent.new :update, [:global,:product]
|
92
|
+
|
93
|
+
subject.routes_for(event) do |klass, method|
|
94
|
+
controller = klass.new
|
95
|
+
controller.action_executed
|
96
|
+
controller.class.should == ProductController
|
97
|
+
method.should == :update_product
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should yield the controller and action name for each route defined with a string for an event" do
|
103
|
+
ComplexProductController.any_instance.should_receive(:action_executed)
|
104
|
+
event = HelperMethods::MockEvent.new :simplify, [:global,:complex_product]
|
105
|
+
|
106
|
+
subject.routes_for(event) do |klass, method|
|
107
|
+
controller = klass.new
|
108
|
+
controller.action_executed
|
109
|
+
controller.class.should == ComplexProductController
|
110
|
+
method.should == :simplify
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module WebsocketRails
|
4
|
+
describe EventQueue do
|
5
|
+
|
6
|
+
describe "#initialize" do
|
7
|
+
it "should create an empty queue" do
|
8
|
+
subject.queue.should == []
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#<<" do
|
13
|
+
it "should add the item to the queue" do
|
14
|
+
subject << 'event'
|
15
|
+
subject.queue.should == ['event']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#flush" do
|
20
|
+
before do
|
21
|
+
subject.queue << 'event'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should yield all items in the queue" do
|
25
|
+
subject.flush do |event|
|
26
|
+
event.should == 'event'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should empty the queue" do
|
31
|
+
subject.flush
|
32
|
+
subject.queue.should == []
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|