websocket-rails 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/CHANGELOG.md +41 -0
  2. data/README.md +1 -1
  3. data/lib/generators/websocket_rails/install/templates/events.rb +15 -5
  4. data/lib/rails/tasks/websocket_rails.tasks +8 -10
  5. data/lib/spec_helpers/matchers/route_matchers.rb +2 -1
  6. data/lib/spec_helpers/spec_helper_event.rb +4 -0
  7. data/lib/websocket-rails.rb +50 -73
  8. data/lib/websocket_rails/base_controller.rb +9 -7
  9. data/lib/websocket_rails/channel.rb +8 -1
  10. data/lib/websocket_rails/channel_manager.rb +6 -0
  11. data/lib/websocket_rails/configuration.rb +112 -0
  12. data/lib/websocket_rails/connection_adapters.rb +13 -4
  13. data/lib/websocket_rails/connection_manager.rb +3 -2
  14. data/lib/websocket_rails/controller_factory.rb +70 -0
  15. data/lib/websocket_rails/data_store.rb +128 -62
  16. data/lib/websocket_rails/dispatcher.rb +17 -16
  17. data/lib/websocket_rails/event.rb +12 -2
  18. data/lib/websocket_rails/event_map.rb +6 -41
  19. data/lib/websocket_rails/internal_events.rb +0 -7
  20. data/lib/websocket_rails/logging.rb +103 -14
  21. data/lib/websocket_rails/synchronization.rb +6 -8
  22. data/lib/websocket_rails/version.rb +1 -1
  23. data/spec/dummy/app/controllers/chat_controller.rb +6 -14
  24. data/spec/dummy/log/test.log +0 -750
  25. data/spec/integration/connection_manager_spec.rb +8 -1
  26. data/spec/spec_helper.rb +3 -16
  27. data/spec/spec_helpers/matchers/route_matchers_spec.rb +2 -11
  28. data/spec/spec_helpers/matchers/trigger_matchers_spec.rb +2 -12
  29. data/spec/unit/channel_manager_spec.rb +8 -0
  30. data/spec/unit/channel_spec.rb +16 -2
  31. data/spec/unit/connection_adapters_spec.rb +32 -11
  32. data/spec/unit/controller_factory_spec.rb +63 -0
  33. data/spec/unit/data_store_spec.rb +91 -24
  34. data/spec/unit/dispatcher_spec.rb +6 -11
  35. data/spec/unit/event_map_spec.rb +17 -27
  36. data/spec/unit/event_spec.rb +14 -0
  37. data/spec/unit/logging_spec.rb +122 -17
  38. 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
- end
38
-
39
- def silence_output
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
@@ -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
- subject.should_receive(:send_data).with('event')
41
- subject.trigger_event 'event'
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 adapters" do
38
- it "should register themselves in the adapters array when inherited" do
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
- describe DataStore do
5
- let(:attribute) {"example_attribute"}
6
- let(:value) {1}
7
-
8
- before(:each) do
9
- @base = double('base_controller')
10
- @base.stub(:client_id).and_return(1)
11
- @data_store = DataStore.new(@base)
12
- end
13
-
14
- it "loads up" do
15
- @data_store.present?.should be_true
16
- end
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
- describe "#[]" do
19
- context "with an undefined attribute" do
20
- it "returns nil" do
21
- @data_store[attribute].should be_nil
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
- context "with a defined attribute" do
26
- it "returns its value" do
27
- @data_store[attribute] = value
28
- @data_store[attribute].should == value
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 "#[]=" do
34
- it "returns the value" do
35
- (@data_store[attribute]=value).should == value
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 a message for an event" do
46
+ context "dispatching an event" do
47
47
  before do
48
- @target = EventTarget.new
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
- @target.should_receive(:test_method)
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