websocket-rails 0.3.0 → 0.4.0
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 +41 -0
- data/README.md +1 -1
- data/lib/generators/websocket_rails/install/templates/events.rb +15 -5
- data/lib/rails/tasks/websocket_rails.tasks +8 -10
- data/lib/spec_helpers/matchers/route_matchers.rb +2 -1
- data/lib/spec_helpers/spec_helper_event.rb +4 -0
- data/lib/websocket-rails.rb +50 -73
- data/lib/websocket_rails/base_controller.rb +9 -7
- data/lib/websocket_rails/channel.rb +8 -1
- data/lib/websocket_rails/channel_manager.rb +6 -0
- data/lib/websocket_rails/configuration.rb +112 -0
- data/lib/websocket_rails/connection_adapters.rb +13 -4
- data/lib/websocket_rails/connection_manager.rb +3 -2
- data/lib/websocket_rails/controller_factory.rb +70 -0
- data/lib/websocket_rails/data_store.rb +128 -62
- data/lib/websocket_rails/dispatcher.rb +17 -16
- data/lib/websocket_rails/event.rb +12 -2
- data/lib/websocket_rails/event_map.rb +6 -41
- data/lib/websocket_rails/internal_events.rb +0 -7
- data/lib/websocket_rails/logging.rb +103 -14
- data/lib/websocket_rails/synchronization.rb +6 -8
- data/lib/websocket_rails/version.rb +1 -1
- data/spec/dummy/app/controllers/chat_controller.rb +6 -14
- data/spec/dummy/log/test.log +0 -750
- data/spec/integration/connection_manager_spec.rb +8 -1
- data/spec/spec_helper.rb +3 -16
- data/spec/spec_helpers/matchers/route_matchers_spec.rb +2 -11
- data/spec/spec_helpers/matchers/trigger_matchers_spec.rb +2 -12
- data/spec/unit/channel_manager_spec.rb +8 -0
- data/spec/unit/channel_spec.rb +16 -2
- data/spec/unit/connection_adapters_spec.rb +32 -11
- data/spec/unit/controller_factory_spec.rb +63 -0
- data/spec/unit/data_store_spec.rb +91 -24
- data/spec/unit/dispatcher_spec.rb +6 -11
- data/spec/unit/event_map_spec.rb +17 -27
- data/spec/unit/event_spec.rb +14 -0
- data/spec/unit/logging_spec.rb +122 -17
- metadata +11 -6
@@ -9,7 +9,7 @@ module WebsocketRails
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def define_test_events
|
12
|
-
WebsocketRails.route_block = nil
|
12
|
+
WebsocketRails.config.route_block = nil
|
13
13
|
WebsocketRails::EventMap.describe do
|
14
14
|
subscribe :client_connected, :to => ChatController, :with_method => :new_user
|
15
15
|
subscribe :change_username, :to => ChatController, :with_method => :change_username
|
@@ -99,6 +99,13 @@ module WebsocketRails
|
|
99
99
|
@server.call( env )
|
100
100
|
socket.on_close
|
101
101
|
end
|
102
|
+
|
103
|
+
it "should unsubscribe from channels" do
|
104
|
+
channel = WebsocketRails[:test_chan]
|
105
|
+
@server.call( env )
|
106
|
+
channel.should_receive(:unsubscribe).with(socket)
|
107
|
+
socket.on_close
|
108
|
+
end
|
102
109
|
end
|
103
110
|
end
|
104
111
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -34,21 +34,8 @@ RSpec.configure do |config|
|
|
34
34
|
# rspec-rails.
|
35
35
|
config.infer_base_class_for_anonymous_controllers = false
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@orig_stderr = $stderr
|
41
|
-
@orig_stdout = $stdout
|
42
|
-
|
43
|
-
# redirect stderr and stdout to /dev/null
|
44
|
-
$stderr = File.new('/dev/null', 'w')
|
45
|
-
$stdout = File.new('/dev/null', 'w')
|
46
|
-
end
|
37
|
+
config.before(:each) do
|
38
|
+
WebsocketRails.config.logger = Logger.new(StringIO.new)
|
39
|
+
end
|
47
40
|
|
48
|
-
# Replace stdout and stderr so anything else is output correctly.
|
49
|
-
def enable_output
|
50
|
-
$stderr = @orig_stderr
|
51
|
-
$stdout = @orig_stdout
|
52
|
-
@orig_stderr = nil
|
53
|
-
@orig_stdout = nil
|
54
41
|
end
|
@@ -19,9 +19,8 @@ describe 'Route Matchers' do
|
|
19
19
|
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
22
|
def define_route_test_events
|
24
|
-
WebsocketRails.route_block = nil
|
23
|
+
WebsocketRails.config.route_block = nil
|
25
24
|
WebsocketRails::EventMap.describe do
|
26
25
|
|
27
26
|
namespace :product do
|
@@ -106,13 +105,5 @@ describe 'Route Matchers' do
|
|
106
105
|
matcher.should produce_as_description 'be routed only to target route_spec_product#update_product'
|
107
106
|
end
|
108
107
|
|
109
|
-
|
110
108
|
end
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
109
|
+
end
|
@@ -20,7 +20,7 @@ describe 'Trigger Matchers' do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def define_test_events
|
23
|
-
WebsocketRails.route_block = nil
|
23
|
+
WebsocketRails.config.route_block = nil
|
24
24
|
WebsocketRails::EventMap.describe do
|
25
25
|
|
26
26
|
namespace :product do
|
@@ -32,16 +32,6 @@ describe 'Trigger Matchers' do
|
|
32
32
|
|
33
33
|
before { define_test_events }
|
34
34
|
|
35
|
-
around(:each) do |example|
|
36
|
-
EM.run do
|
37
|
-
example.run
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
after(:each) do
|
42
|
-
EM.stop
|
43
|
-
end
|
44
|
-
|
45
35
|
# as we have have 16 possible combinations of trigger messages and data matching pattern (data|no_data, success|failure,
|
46
36
|
# no_checking|checking_with_any|checking_with_nil|checking_with_exact_data) plus the case of no message at all
|
47
37
|
# for EACH of the matchers, resulting in a total 51 cases, we will not extensively test all cases for all matchers
|
@@ -254,4 +244,4 @@ describe 'Trigger Matchers' do
|
|
254
244
|
end
|
255
245
|
|
256
246
|
|
257
|
-
end
|
247
|
+
end
|
@@ -25,5 +25,13 @@ module WebsocketRails
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
describe "unsubscribe" do
|
29
|
+
it "should unsubscribe connection from all channels" do
|
30
|
+
subject[:awesome_channel].should_receive(:unsubscribe).with(:some_connection)
|
31
|
+
subject[:awesome_channel]
|
32
|
+
subject.unsubscribe(:some_connection)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
28
36
|
end
|
29
37
|
end
|
data/spec/unit/channel_spec.rb
CHANGED
@@ -21,6 +21,19 @@ module WebsocketRails
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
describe "#unsubscribe" do
|
25
|
+
it "should remove connection from subscriber pool" do
|
26
|
+
subject.subscribe connection
|
27
|
+
subject.unsubscribe connection
|
28
|
+
subject.subscribers.include?(connection).should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should do nothing if connection is not subscribed to channel" do
|
32
|
+
subject.unsubscribe connection
|
33
|
+
subject.subscribers.include?(connection).should be_false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
24
37
|
describe "#trigger" do
|
25
38
|
it "should create a new event and trigger it on all subscribers" do
|
26
39
|
event = double('event').as_null_object
|
@@ -37,8 +50,9 @@ module WebsocketRails
|
|
37
50
|
|
38
51
|
describe "#trigger_event" do
|
39
52
|
it "should forward the event to the subscribers" do
|
40
|
-
|
41
|
-
subject.
|
53
|
+
event = double('event').as_null_object
|
54
|
+
subject.should_receive(:send_data).with(event)
|
55
|
+
subject.trigger_event event
|
42
56
|
end
|
43
57
|
end
|
44
58
|
|
@@ -1,31 +1,31 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module WebsocketRails
|
4
|
-
|
4
|
+
|
5
5
|
class ConnectionAdapters::Test < ConnectionAdapters::Base
|
6
6
|
def self.accepts?(env)
|
7
7
|
true
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
describe ConnectionAdapters do
|
12
|
-
|
12
|
+
|
13
13
|
context ".register" do
|
14
14
|
it "should store a reference to the adapter in the adapters array" do
|
15
15
|
ConnectionAdapters.register( ConnectionAdapters::Test )
|
16
16
|
ConnectionAdapters.adapters.include?( ConnectionAdapters::Test ).should be_true
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
context ".establish_connection" do
|
21
21
|
it "should return the correct connection adapter instance" do
|
22
22
|
adapter = ConnectionAdapters.establish_connection( mock_request, double('Dispatcher').as_null_object )
|
23
23
|
adapter.class.should == ConnectionAdapters::Test
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
module ConnectionAdapters
|
30
30
|
describe Base do
|
31
31
|
let(:dispatcher) { double('Dispatcher').as_null_object }
|
@@ -33,12 +33,16 @@ module WebsocketRails
|
|
33
33
|
let(:event) { double('Event').as_null_object }
|
34
34
|
before { Event.stub(:new_from_json).and_return(event) }
|
35
35
|
subject { Base.new( mock_request, dispatcher ) }
|
36
|
-
|
37
|
-
context "new
|
38
|
-
it "should register
|
36
|
+
|
37
|
+
context "new adapter" do
|
38
|
+
it "should register itself in the adapters array when inherited" do
|
39
39
|
adapter = Class.new( ConnectionAdapters::Base )
|
40
40
|
ConnectionAdapters.adapters.include?( adapter ).should be_true
|
41
41
|
end
|
42
|
+
|
43
|
+
it "should create a new DataStore::Connection instance" do
|
44
|
+
subject.data_store.should be_a DataStore::Connection
|
45
|
+
end
|
42
46
|
end
|
43
47
|
|
44
48
|
describe "#on_open" do
|
@@ -82,13 +86,13 @@ module WebsocketRails
|
|
82
86
|
subject.on_error("test_data")
|
83
87
|
end
|
84
88
|
end
|
85
|
-
|
89
|
+
|
86
90
|
describe "#send" do
|
87
91
|
it "should raise a NotImplementedError exception" do
|
88
92
|
expect { subject.send :message }.to raise_exception( NotImplementedError )
|
89
93
|
end
|
90
94
|
end
|
91
|
-
|
95
|
+
|
92
96
|
describe "#enqueue" do
|
93
97
|
it "should add the event to the queue" do
|
94
98
|
subject.enqueue 'event'
|
@@ -125,6 +129,23 @@ module WebsocketRails
|
|
125
129
|
subject.flush
|
126
130
|
end
|
127
131
|
end
|
132
|
+
|
133
|
+
describe "#close_connection" do
|
134
|
+
before do
|
135
|
+
@connection_manager = double('connection_manager').as_null_object
|
136
|
+
subject.stub_chain(:dispatcher, :connection_manager).and_return(@connection_manager)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "calls delegates to the conection manager" do
|
140
|
+
@connection_manager.should_receive(:close_connection).with(subject)
|
141
|
+
subject.__send__(:close_connection)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "deletes it's data_store" do
|
145
|
+
subject.data_store.should_receive(:destroy!)
|
146
|
+
subject.__send__(:close_connection)
|
147
|
+
end
|
148
|
+
end
|
128
149
|
end
|
129
150
|
end
|
130
151
|
end
|
@@ -0,0 +1,63 @@
|
|
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
|
+
it "creates and returns a new controller instance" do
|
35
|
+
controller = subject.new_for_event(event, TestController)
|
36
|
+
controller.class.should == TestController
|
37
|
+
end
|
38
|
+
|
39
|
+
it "initializes the controller with the correct data_store" do
|
40
|
+
store = double('data_store')
|
41
|
+
subject.controller_stores[TestController] = store
|
42
|
+
controller = subject.new_for_event(event, TestController)
|
43
|
+
controller.controller_store.should == store
|
44
|
+
end
|
45
|
+
|
46
|
+
it "initializes the controller with the correct event" do
|
47
|
+
controller = subject.new_for_event(event, TestController)
|
48
|
+
controller.event.should == event
|
49
|
+
end
|
50
|
+
|
51
|
+
it "initializes the controller with the correct dispatcher" do
|
52
|
+
controller = subject.new_for_event(event, TestController)
|
53
|
+
controller._dispatcher.should == dispatcher
|
54
|
+
end
|
55
|
+
|
56
|
+
it "calls #initialize_session on the controller only once" do
|
57
|
+
TestController.any_instance.should_receive(:initialize_session).once
|
58
|
+
3.times { subject.new_for_event(event, TestController) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -1,39 +1,106 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module WebsocketRails
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
17
25
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
22
36
|
end
|
23
37
|
end
|
24
38
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@
|
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
|
29
76
|
end
|
30
77
|
end
|
31
78
|
end
|
32
79
|
|
33
|
-
describe
|
34
|
-
|
35
|
-
|
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
|
36
102
|
end
|
37
103
|
end
|
38
104
|
end
|
105
|
+
|
39
106
|
end
|
@@ -4,7 +4,7 @@ require 'support/mock_web_socket'
|
|
4
4
|
module WebsocketRails
|
5
5
|
|
6
6
|
class EventTarget
|
7
|
-
attr_reader :_event, :test_method
|
7
|
+
attr_reader :_event, :_dispatcher, :test_method
|
8
8
|
|
9
9
|
def execute_observers(event_name)
|
10
10
|
true
|
@@ -13,7 +13,7 @@ module WebsocketRails
|
|
13
13
|
|
14
14
|
describe Dispatcher do
|
15
15
|
|
16
|
-
let(:event) { double('Event') }
|
16
|
+
let(:event) { double('Event').as_null_object }
|
17
17
|
let(:connection) { MockWebSocket.new }
|
18
18
|
let(:connection_manager) { double('connection_manager').as_null_object }
|
19
19
|
subject { Dispatcher.new(connection_manager) }
|
@@ -43,27 +43,22 @@ module WebsocketRails
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
context "dispatching
|
46
|
+
context "dispatching an event" do
|
47
47
|
before do
|
48
|
-
|
49
|
-
EventMap.any_instance.stub(:routes_for).with(any_args).and_yield( @target, :test_method )
|
48
|
+
EventMap.any_instance.stub(:routes_for).with(any_args).and_yield(EventTarget, :test_method)
|
50
49
|
event.stub(:name).and_return(:test_method)
|
51
50
|
event.stub(:data).and_return(:some_message)
|
52
51
|
event.stub(:connection).and_return(connection)
|
53
52
|
event.stub(:is_channel?).and_return(false)
|
54
53
|
event.stub(:is_invalid?).and_return(false)
|
54
|
+
event.stub(:is_internal?).and_return(false)
|
55
55
|
end
|
56
56
|
|
57
57
|
it "should execute the correct method on the target class" do
|
58
|
-
|
58
|
+
EventTarget.any_instance.should_receive(:test_method)
|
59
59
|
subject.dispatch(event)
|
60
60
|
end
|
61
61
|
|
62
|
-
it "should set the _event instance variable on the target object" do
|
63
|
-
subject.dispatch(event)
|
64
|
-
@target._event.should == event
|
65
|
-
end
|
66
|
-
|
67
62
|
context "channel events" do
|
68
63
|
it "should forward the data to the correct channel" do
|
69
64
|
event = Event.new 'test', :data => 'data', :channel => :awesome_channel
|