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,181 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module WebsocketRails
|
4
|
+
describe Event do
|
5
|
+
let(:encoded_message) { '["new_message",{"id":"1234","data":{"message":"this is a message"}}]' }
|
6
|
+
let(:encoded_message_string) { '["new_message",{"id":"1234","data":"this is a message"}]' }
|
7
|
+
let(:namespace_encoded_message_string) { '["product.new_message",{"id":"1234","data":"this is a message"}]' }
|
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","user_id":null,"data":"this is a message","success":null,"result":null,"token":null,"server_token":"1234"}]' }
|
10
|
+
let(:synchronizable_encoded_message) { '["new_message",{"id":"1234","data":{"message":"this is a message"},"server_token":"1234"}]' }
|
11
|
+
let(:connection) { double('connection') }
|
12
|
+
let(:wrongly_encoded_message) { '["new_message",[{"id":"1234","data":{"message":"this is a message"}}]]' }
|
13
|
+
|
14
|
+
before { connection.stub(:id).and_return(1) }
|
15
|
+
|
16
|
+
describe ".new_from_json" do
|
17
|
+
context "messages in the global namespace" do
|
18
|
+
it "should decode a new message and store it" do
|
19
|
+
event = Event.new_from_json( encoded_message, connection )
|
20
|
+
event.connection.should == connection
|
21
|
+
event.name.should == :new_message
|
22
|
+
event.namespace.should == [:global]
|
23
|
+
event.data[:message].should == 'this is a message'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "messages in a child namespace" do
|
28
|
+
it "should store the event with the correct namesapce" do
|
29
|
+
event = Event.new_from_json( namespace_encoded_message, connection )
|
30
|
+
event.namespace.should == [:global,:product]
|
31
|
+
event.name.should == :new_message
|
32
|
+
event.data[:message].should == 'this is a message'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "invalid messages" do
|
37
|
+
it "should return an invalid event if data is wrongly encoded" do
|
38
|
+
event = Event.new_from_json( wrongly_encoded_message, connection )
|
39
|
+
event.is_invalid?.should be_true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".new_on_open" do
|
45
|
+
before { @event = Event.new_on_open connection, {:message => 'connected'} }
|
46
|
+
|
47
|
+
it "should create an event named :client_connected" do
|
48
|
+
@event.name.should == :client_connected
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should send the connection id to the client" do
|
52
|
+
@event.data[:connection_id].should == 1
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should merge the optional data with the connection id" do
|
56
|
+
@event.data[:message].should == 'connected'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe ".new_on_close" do
|
61
|
+
it "should create an event named :client_disconnected" do
|
62
|
+
event = Event.new_on_close( connection, "optional_data" )
|
63
|
+
event.name.should == :client_disconnected
|
64
|
+
event.data.should == "optional_data"
|
65
|
+
event.connection.should == connection
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ".new_on_error" do
|
70
|
+
it "should create an event named :client_error" do
|
71
|
+
event = Event.new_on_error( connection, "optional_data" )
|
72
|
+
event.name.should == :client_error
|
73
|
+
event.data.should == "optional_data"
|
74
|
+
event.connection.should == connection
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "new namespaced events" do
|
79
|
+
it "should store the namespace in the namespace attribute" do
|
80
|
+
event = Event.new "event", :data => {}, :connection => connection, :namespace => :product
|
81
|
+
event.namespace.should == [:global,:product]
|
82
|
+
event.name.should == :event
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should store nested namespaces in the namespace attribute" do
|
86
|
+
event = Event.new "event", :data => {}, :connection => connection, :namespace => [:product,:x_ray_vision]
|
87
|
+
event.namespace.should == [:global,:product,:x_ray_vision]
|
88
|
+
event.name.should == :event
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "new channel events" do
|
93
|
+
it "should store the channel name in the channel attribute" do
|
94
|
+
event = Event.new "event", :data => {}, :connection => connection, :channel => :awesome_channel
|
95
|
+
event.channel.should == :awesome_channel
|
96
|
+
event.name.should == :event
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should not raise an error if the channel name cannot be symbolized" do
|
100
|
+
expect { Event.new "event", :data => {}, :connection => connection, :channel => 5 }.to_not raise_error
|
101
|
+
event = Event.new "event", :data => {}, :connection => connection, :channel => 5
|
102
|
+
event.channel.should == :"5"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#is_channel?" do
|
107
|
+
it "should return true if an event belongs to a channel" do
|
108
|
+
event = Event.new "event", :data => "data", :channel => :awesome_channel
|
109
|
+
event.is_channel?.should be_true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#is_user?" do
|
114
|
+
it "returns true if the event is meant for a specific user" do
|
115
|
+
event = Event.new "event", :data => "data", :user_id => :username
|
116
|
+
event.is_user?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#is_invalid?" do
|
121
|
+
it "returns true if the event name is :invalid_event" do
|
122
|
+
event = Event.new(:invalid_event)
|
123
|
+
event.is_invalid?.should be_true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "#is_internal?" do
|
128
|
+
it "returns true if the event is namespaced under websocket_rails" do
|
129
|
+
event = Event.new(:internal_event, :namespace => :websocket_rails)
|
130
|
+
event.is_internal?.should be_true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "#serialize" do
|
135
|
+
context "messages in the global namespace" do
|
136
|
+
it "should not add the global namespace to the event name" do
|
137
|
+
event = Event.new_from_json encoded_message_string, connection
|
138
|
+
raw_data = event.serialize
|
139
|
+
data = JSON.parse raw_data
|
140
|
+
data[0].should == "new_message"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "messages in a child namespace" do
|
145
|
+
it "should add the namespace to the front of the event name" do
|
146
|
+
event = Event.new_from_json namespace_encoded_message_string, connection
|
147
|
+
raw_data = event.serialize
|
148
|
+
data = JSON.parse raw_data
|
149
|
+
data[0].should == "product.new_message"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "messages for a channel" do
|
154
|
+
it "should add the channel name as the first element of the serialized array" do
|
155
|
+
event = Event.new_from_json channel_encoded_message_string, connection
|
156
|
+
event.serialize.should == channel_encoded_message_string
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "messages for synchronization" do
|
161
|
+
it "should include the unique server token" do
|
162
|
+
event = Event.new_from_json synchronizable_encoded_message, connection
|
163
|
+
raw_data = event.serialize
|
164
|
+
data = JSON.parse raw_data
|
165
|
+
data[1]['server_token'].should == '1234'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "#as_json" do
|
170
|
+
it "returns a Hash representation of the Event" do
|
171
|
+
hash = { data: { 'test' => 'test'}, channel: :awesome_channel }
|
172
|
+
event = Event.new 'test', hash
|
173
|
+
event.as_json[0].should == :test
|
174
|
+
event.as_json[1][:data].should == hash[:data]
|
175
|
+
event.as_json[1][:channel].should == hash[:channel]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "ostruct"
|
3
|
+
|
4
|
+
module WebsocketRails
|
5
|
+
describe Logging do
|
6
|
+
|
7
|
+
class LoggedClass
|
8
|
+
include Logging
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:io) { StringIO.new }
|
12
|
+
let(:object) { LoggedClass.new }
|
13
|
+
|
14
|
+
before do
|
15
|
+
WebsocketRails.config.logger = Logger.new(io)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#info" do
|
19
|
+
it "logs the message" do
|
20
|
+
object.info "info logged"
|
21
|
+
io.string.should include("info logged")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#debug" do
|
26
|
+
it "logs the message" do
|
27
|
+
object.debug "debug logged"
|
28
|
+
io.string.should include("debug logged")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "log_exception" do
|
33
|
+
let(:exception) { Exception.new('kaputt!').tap { |e| e.set_backtrace(['line 1', 'line 2']) } }
|
34
|
+
|
35
|
+
it "logs the exception message" do
|
36
|
+
object.log_exception(exception)
|
37
|
+
io.string.should include('kaputt!')
|
38
|
+
end
|
39
|
+
|
40
|
+
it "logs the backtrace" do
|
41
|
+
object.log_exception(exception)
|
42
|
+
io.string.should include("line 1")
|
43
|
+
io.string.should include("line 2")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "logging an event" do
|
48
|
+
before do
|
49
|
+
data = {
|
50
|
+
namespace: :logger,
|
51
|
+
data: {message: "hello"},
|
52
|
+
connection: double('connection')
|
53
|
+
}
|
54
|
+
@event = Event.new(:logged_event, data)
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#log_event_start" do
|
58
|
+
it "logs the event information" do
|
59
|
+
object.log_event_start(@event)
|
60
|
+
io.string.should include("Started Event:")
|
61
|
+
io.string.should include("logger.logged_event")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#log_event_end" do
|
66
|
+
it "logs the total time the event took to process" do
|
67
|
+
time = 12
|
68
|
+
object.log_event_end(@event, time)
|
69
|
+
io.string.should include("Event #{@event.encoded_name} Finished in #{time.to_f.to_d.to_s} seconds")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#log_event" do
|
74
|
+
it "logs the start of the event" do
|
75
|
+
object.should_receive(:log_event_start).with(@event)
|
76
|
+
object.log_event(@event) { true }
|
77
|
+
end
|
78
|
+
|
79
|
+
it "logs the end of the event" do
|
80
|
+
time = Time.now
|
81
|
+
Time.stub(:now).and_return(time)
|
82
|
+
object.should_receive(:log_event_end).with(@event, 0)
|
83
|
+
object.log_event(@event) { true }
|
84
|
+
end
|
85
|
+
|
86
|
+
it "executes the block" do
|
87
|
+
executed = false
|
88
|
+
object.log_event(@event) { executed = true }
|
89
|
+
executed.should == true
|
90
|
+
end
|
91
|
+
|
92
|
+
it "logs any exceptions" do
|
93
|
+
exception = Exception.new("ouch")
|
94
|
+
object.should_receive(:log_exception).with(exception)
|
95
|
+
expect {
|
96
|
+
object.log_event(@event) { raise exception }
|
97
|
+
}.to raise_exception(exception.class, exception.message)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#log_data?" do
|
103
|
+
before do
|
104
|
+
@hash_event = Event.new(:log_test, :data => {test: true})
|
105
|
+
@string_event = Event.new(:log_test, :data => "message")
|
106
|
+
@object_event = Event.new(:log_test, :data => OpenStruct.new)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "returns true if data is an allowed type" do
|
110
|
+
object.log_data?(@hash_event).should == true
|
111
|
+
object.log_data?(@string_event).should == true
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns false if the data is not an allows type" do
|
115
|
+
object.log_data?(@object_event).should == false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#log_event?" do
|
120
|
+
context "with an internal event" do
|
121
|
+
before do
|
122
|
+
@event = Event.new(:internal, :namespace => :websocket_rails)
|
123
|
+
end
|
124
|
+
|
125
|
+
context "when WebsocketRails.config.log_internal_events? is false" do
|
126
|
+
it "returns false" do
|
127
|
+
WebsocketRails.config.log_internal_events = false
|
128
|
+
object.log_event?(@event).should == false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "when WebsocketRails.config.log_internal_events? is true" do
|
133
|
+
it "returns true" do
|
134
|
+
WebsocketRails.config.log_internal_events = true
|
135
|
+
object.log_event?(@event).should == true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "with an external event" do
|
141
|
+
before do
|
142
|
+
@event = Event.new(:external)
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when WebsocketRails.config.log_internal_events? is false" do
|
146
|
+
it "returns true" do
|
147
|
+
WebsocketRails.config.log_internal_events = false
|
148
|
+
object.log_event?(@event).should == true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "when WebsocketRails.config.log_internal_events? is true" do
|
153
|
+
it "returns true" do
|
154
|
+
WebsocketRails.config.log_internal_events = true
|
155
|
+
object.log_event?(@event).should == true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "eventmachine"
|
3
|
+
require "ostruct"
|
4
|
+
|
5
|
+
module WebsocketRails
|
6
|
+
describe Synchronization do
|
7
|
+
|
8
|
+
around(:each) do |example|
|
9
|
+
EM.run do
|
10
|
+
Fiber.new do
|
11
|
+
@redis = Redis.new(WebsocketRails.config.redis_options)
|
12
|
+
@redis.del "websocket_rails.active_servers"
|
13
|
+
example.run
|
14
|
+
end.resume
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
after(:each) do
|
19
|
+
@redis.del "websocket_rails.active_servers"
|
20
|
+
EM.stop
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:subject) { Synchronization.singleton }
|
24
|
+
|
25
|
+
describe "#publish" do
|
26
|
+
it "should add the serialized event to the websocket_rails.events channel" do
|
27
|
+
event = Event.new(:test_event, :channel => 'synchrony', :data => 'hello channel')
|
28
|
+
Redis.any_instance.should_receive(:publish).with("websocket_rails.events", event.serialize)
|
29
|
+
|
30
|
+
subject.publish(event)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#synchronize!" do
|
35
|
+
# need to add an integration test to cover this.
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#trigger_incoming" do
|
39
|
+
context "when dispatching channel events" do
|
40
|
+
before do
|
41
|
+
@event = Event.new(:channel_event, :channel => :channel_one, :data => 'hello channel one')
|
42
|
+
end
|
43
|
+
|
44
|
+
it "triggers the event on the correct channel" do
|
45
|
+
WebsocketRails[:channel_one].should_receive(:trigger_event).with @event
|
46
|
+
subject.trigger_incoming @event
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when dispatching user events" do
|
51
|
+
before do
|
52
|
+
@event = Event.new(:channel_event, :user_id => "username", :data => 'hello channel one')
|
53
|
+
end
|
54
|
+
|
55
|
+
context "and the user is not connected to this server" do
|
56
|
+
it "does nothing" do
|
57
|
+
subject.trigger_incoming(@event).should == nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "and the user is connected to this server" do
|
62
|
+
before do
|
63
|
+
@connection = double('Connection')
|
64
|
+
WebsocketRails.users["username"] = @connection
|
65
|
+
end
|
66
|
+
|
67
|
+
it "triggers the event on the correct user" do
|
68
|
+
WebsocketRails.users["username"].should_receive(:trigger).with @event
|
69
|
+
subject.trigger_incoming @event
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#generate_server_token" do
|
76
|
+
before do
|
77
|
+
SecureRandom.stub(:urlsafe_base64).and_return(1, 2, 3)
|
78
|
+
end
|
79
|
+
|
80
|
+
after do
|
81
|
+
@redis.del "websocket_rails.active_servers"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should generate a unique server token" do
|
85
|
+
SecureRandom.should_receive(:urlsafe_base64).at_least(1).times
|
86
|
+
subject.generate_server_token
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should generate another id if the current id is already registered" do
|
90
|
+
@redis.sadd "websocket_rails.active_servers", 1
|
91
|
+
token = subject.generate_server_token
|
92
|
+
token.should == 2
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#register_server" do
|
97
|
+
it "should add the unique token to the active_servers key in redis" do
|
98
|
+
Redis.any_instance.should_receive(:sadd).with("websocket_rails.active_servers", "token")
|
99
|
+
subject.register_server "token"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#remove_server" do
|
104
|
+
it "should remove the unique token from the active_servers key in redis" do
|
105
|
+
Redis.any_instance.should_receive(:srem).with("websocket_rails.active_servers", "token")
|
106
|
+
subject.remove_server "token"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#register_user" do
|
111
|
+
before do
|
112
|
+
@connection = double('Connection')
|
113
|
+
@user = User.new
|
114
|
+
@user.attributes.update(name: 'Frank The Tank', email: 'frank@tank.com')
|
115
|
+
@user.instance_variable_set(:@new_record, false)
|
116
|
+
@user.instance_variable_set(:@destroyed, false)
|
117
|
+
@connection.stub(:user_identifier).and_return 'Frank The Tank'
|
118
|
+
@connection.stub(:user).and_return @user
|
119
|
+
end
|
120
|
+
|
121
|
+
it "stores the serialized user object in redis" do
|
122
|
+
@user.persisted?.should == true
|
123
|
+
Redis.any_instance.should_receive(:hset).with("websocket_rails.users", @connection.user_identifier, @user.as_json.to_json)
|
124
|
+
Synchronization.register_user(@connection)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#destroy_user" do
|
129
|
+
it "stores the serialized user object in redis" do
|
130
|
+
Redis.any_instance.should_receive(:hdel).with("websocket_rails.users", 'user_id')
|
131
|
+
Synchronization.destroy_user('user_id')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "#find_user" do
|
136
|
+
it "retrieves the serialized user object in redis" do
|
137
|
+
Redis.any_instance.should_receive(:hget).with("websocket_rails.users", 'test')
|
138
|
+
Synchronization.find_user('test')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "#all_users" do
|
143
|
+
it "retrieves the entire serialized users hash redis" do
|
144
|
+
Redis.any_instance.should_receive(:hgetall).with("websocket_rails.users")
|
145
|
+
Synchronization.all_users
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class ComplexProductController < WebsocketRails::BaseController
|
5
|
+
|
6
|
+
def simplify
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module MyModule
|
12
|
+
|
13
|
+
class AnotherController < WebsocketRails::BaseController
|
14
|
+
|
15
|
+
def complicate
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
module MySubModule
|
21
|
+
|
22
|
+
class AThirdController < WebsocketRails::BaseController
|
23
|
+
|
24
|
+
def confuse
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
module WebsocketRails
|
35
|
+
|
36
|
+
describe TargetValidator do
|
37
|
+
|
38
|
+
describe 'validate_target' do
|
39
|
+
|
40
|
+
it 'should raise an error when target class is not supported' do
|
41
|
+
expect{TargetValidator.validate_target(50)}.to raise_error
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should raise if passed hash does not contain the to: key' do
|
45
|
+
expect{TargetValidator.validate_target(from: ComplexProductController, with_method: :simplify)}.to raise_error
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should raise if passed hash does not contain the with_method: key' do
|
49
|
+
expect{TargetValidator.validate_target(to: ComplexProductController, without_method: :simplify)}.to raise_error
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should raise if the string is not in the correct format' do
|
53
|
+
expect{TargetValidator.validate_target('malformed_string')}.to raise_error
|
54
|
+
expect{TargetValidator.validate_target('very#malformed#string')}.to raise_error
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should raise if the class specified in the String does not exist' do
|
58
|
+
expect{TargetValidator.validate_target('my_non_existent#my_method')}.to raise_error
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should parse correctly a well-formed Hash' do
|
62
|
+
TargetValidator::validate_target(to: ComplexProductController, with_method: :simplify).should == [ComplexProductController, :simplify]
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when the string is well-formed' do
|
66
|
+
|
67
|
+
it 'should parse correctly when the controller is a top-level' do
|
68
|
+
TargetValidator::validate_target('complex_product#simplify').should == [ComplexProductController, :simplify]
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should parse correctly when the controller belongs to a module' do
|
72
|
+
TargetValidator::validate_target('my_module/another#complicate').should == [MyModule::AnotherController, :complicate]
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should parse correctly with many levels of module nesting' do
|
76
|
+
TargetValidator::validate_target('my_module/my_sub_module/a_third#confuse').should == [MyModule::MySubModule::AThirdController, :confuse]
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
end
|