Skip to content

Commit 5485d14

Browse files
committed
Complete rewrite of the ConnectionManager Rack application.
Completely rewrote ConnectionManager to remove the websocket-rack dependency. ConnectionManager is now a standalone Rack application using the faye-websocket gem. Added close to complete test coverage. Reworked the Dispatcher class to focus it's responsibilities.
1 parent aaf0905 commit 5485d14

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+10266
-165
lines changed

.DS_Store

6 KB
Binary file not shown.

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.bundle/
22
log/*.log
33
pkg/
4+
doc/
45
test/dummy/db/*.sqlite3
56
test/dummy/log/*.log
67
test/dummy/tmp/

.rspec

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
--color
2+
--format documentation

.yardoc/checksums

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
lib/websocket-rails.rb 6eee31896faf6bad2aa70a6e56963c3451e0e95a
2+
lib/websocket_rails/engine.rb 26d86b36fb5044066fe5fdb752ba2688c6af0e96
3+
lib/websocket_rails/version.rb 18f7775ac039b0fd3d0db90733e892b10405e1cf
4+
lib/websocket_rails/data_store.rb 3900177a989eb5d600a357596de6d13aec49dad2
5+
lib/websocket_rails/dispatcher.rb d075bf5431a7a61275b2190006d42eee6267fce7
6+
lib/websocket_rails/base_controller.rb bf11e6dfd5a2f1d5782f5da45840fedbf573fde1
7+
lib/websocket_rails/extensions/common.rb fd633e01c22e718a4955eabca9c204027b0c348f
8+
lib/websocket_rails/connection_manager.rb 9f9a8f743b357616a92881da60e0bbfac8c3bc13
9+
lib/websocket_rails/extensions/websocket_rack.rb c5ba8d6243b4ca201a2e56920ef142279d943227

.yardoc/object_types

2.66 KB
Binary file not shown.

.yardoc/objects/root.dat

22.1 KB
Binary file not shown.

.yardoc/proxy_types

74 Bytes
Binary file not shown.

Gemfile

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
source "http://rubygems.org"
22

3-
gemspec
3+
gemspec
4+
5+
gem "rspec-rails"
6+
gem "eventmachine", ">= 1.0.0.beta.3"
7+
gem "faye-websocket"
8+
gem "em-websocket-request"

Gemfile.lock

+34-10
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ PATH
22
remote: .
33
specs:
44
websocket-rails (0.0.1)
5-
em-websocket (~> 0.3.6)
5+
faye-websocket
66
rack
77
thin
8-
websocket-rack
98

109
GEM
1110
remote: http://rubygems.org/
@@ -40,13 +39,25 @@ GEM
4039
addressable (2.2.7)
4140
arel (2.0.10)
4241
builder (2.1.2)
42+
cookiejar (0.3.0)
4343
daemons (1.1.8)
44-
em-websocket (0.3.6)
45-
addressable (>= 2.1.1)
46-
eventmachine (>= 0.12.9)
44+
diff-lcs (1.1.3)
45+
em-http-request (1.0.2)
46+
addressable (>= 2.2.3)
47+
cookiejar
48+
em-socksify
49+
eventmachine (>= 1.0.0.beta.4)
50+
http_parser.rb (>= 0.5.3)
51+
em-socksify (0.2.0)
52+
eventmachine (>= 1.0.0.beta.4)
53+
em-websocket-request (0.0.7)
54+
em-http-request (~> 1.0.0)
4755
erubis (2.6.6)
4856
abstract (>= 1.0.0)
49-
eventmachine (0.12.10)
57+
eventmachine (1.0.0.beta.4)
58+
faye-websocket (0.4.5)
59+
eventmachine (>= 0.12.0)
60+
http_parser.rb (0.5.3)
5061
i18n (0.5.0)
5162
json (1.6.5)
5263
mail (2.2.19)
@@ -78,6 +89,19 @@ GEM
7889
rake (0.9.2.2)
7990
rdoc (3.12)
8091
json (~> 1.4)
92+
rspec (2.9.0)
93+
rspec-core (~> 2.9.0)
94+
rspec-expectations (~> 2.9.0)
95+
rspec-mocks (~> 2.9.0)
96+
rspec-core (2.9.0)
97+
rspec-expectations (2.9.1)
98+
diff-lcs (~> 1.1.3)
99+
rspec-mocks (2.9.0)
100+
rspec-rails (2.9.0)
101+
actionpack (>= 3.0)
102+
activesupport (>= 3.0)
103+
railties (>= 3.0)
104+
rspec (~> 2.9.0)
81105
sqlite3 (1.3.5)
82106
thin (1.3.1)
83107
daemons (>= 1.0.9)
@@ -88,16 +112,16 @@ GEM
88112
polyglot
89113
polyglot (>= 0.3.1)
90114
tzinfo (0.3.32)
91-
websocket-rack (0.3.3)
92-
em-websocket (~> 0.3.6)
93-
rack
94-
thin
95115

96116
PLATFORMS
97117
ruby
98118

99119
DEPENDENCIES
120+
em-websocket-request
121+
eventmachine (>= 1.0.0.beta.3)
122+
faye-websocket
100123
rails
101124
rake
125+
rspec-rails
102126
sqlite3
103127
websocket-rails!

MIT-LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright 2012 YOURNAME
1+
Copyright 2012 Dan Knox and Kyle Whalen
22

33
Permission is hereby granted, free of charge, to any person obtaining
44
a copy of this software and associated documentation files (the

README.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
# Websocket-Rails
22

3-
Plug and play WebSocket support for ruby on rails. Includes event router for mapping javascript events to controller actions. There is no need for a separate WebSocket server process. Requests to `/websocket` will be passed through to the embedded WebSocket server provided by the em-websocket gem.
3+
Plug and play WebSocket support for ruby on rails. Includes event router for mapping javascript events to controller actions. There is no need for a separate WebSocket server process. Requests to `/websocket` will be passed through to the `ConnectionManager` class which is a simple Rack based WebSocket server developed using the `Faye::WebSocket` library.
44

55
*Important Note*
66

7-
This gem is not even close to production ready. This is mostly a proof of concept as of right now. Please try it out and let me know what you like or dislike. We will be adding much more soon including a development road map and full test coverage.
7+
This is mostly a proof of concept as of right now. Please try it out and let me know what you like or dislike. We will be adding much more soon including a development road map.
8+
9+
*Update*
10+
11+
We are finally very close to the first production release. Any comments or suggestions would be appreciated. The test coverage is now solid and the `ConnectionManager` class has been completely rewritten. There were a few bugs in the connection management on the first release that have been eliminated. Please upgrade to the latest version if you have not yet done so.
812

913
## Installation
1014

@@ -31,7 +35,7 @@ You can subscribe multiple controllers and actions to the same event to provide
3135
````ruby
3236
# app/config/initializers
3337

34-
WebsocketRails::Dispatcher.describe_events do
38+
WebsocketRails::Events.describe_events do
3539
# The :client_connected method is fired automatically when a new client connects
3640
subscribe :client_connected, to: ChatController, with_method: :client_connected
3741

lib/websocket-rails.rb

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require "active_support/dependencies"
2+
require 'thin'
23

34
module WebsocketRails
45
mattr_accessor :app_root
@@ -7,12 +8,17 @@ def self.setup
78
yield self
89
end
910

11+
def self.route_block=(routes)
12+
@event_routes = routes
13+
end
14+
15+
def self.route_block
16+
@event_routes
17+
end
1018
end
1119

1220
require "websocket_rails/engine"
1321
require 'websocket_rails/connection_manager'
1422
require 'websocket_rails/dispatcher'
15-
require 'websocket_rails/base_controller'
16-
require 'websocket_rails/extensions/common'
17-
18-
WebsocketRails::Extensions::Common.apply!
23+
require 'websocket_rails/events'
24+
require 'websocket_rails/base_controller'

lib/websocket_rails/base_controller.rb

+8-5
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@ def initialize
88
@objects
99
end
1010

11+
def connection
12+
@_connection
13+
end
14+
1115
def client_id
12-
@_message.first
16+
connection.object_id
1317
end
14-
18+
1519
def message
16-
@_message.last
20+
@_message
1721
end
1822

1923
def send_message(event, message)
20-
@message_counter += 1
21-
@_dispatcher.send_message event.to_s, [client_id,message] if @_dispatcher.respond_to?(:send_message)
24+
@_dispatcher.send_message event.to_s, message, connection if @_dispatcher.respond_to?(:send_message)
2225
end
2326

2427
def broadcast_message(event, message)
+46-26
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,58 @@
1+
require 'faye/websocket'
12
require 'rack'
2-
require 'rack/websocket'
3-
require 'json'
3+
require 'thin'
4+
45
module WebsocketRails
5-
class ConnectionManager < Rack::WebSocket::Application
6-
def initialize(*args)
7-
@dispatcher = Dispatcher.new(self)
8-
super
9-
end
10-
11-
def on_open(env)
12-
puts "Client connected\n"
13-
@dispatcher.dispatch('client_connected',{},env)
14-
end
6+
class ConnectionManager
157

16-
def on_message(env, msg)
17-
@dispatcher.receive( msg, env )
18-
end
8+
attr_accessor :connections
199

20-
def on_error(env, error)
21-
puts "Error occured: " + error.message
10+
def initialize
11+
@connections = []
12+
@dispatcher = Dispatcher.new( self )
2213
end
2314

24-
def on_close(env)
25-
close_connection(env['websocket.client_id'])
26-
@dispatcher.dispatch('client_disconnected',{},env)
27-
puts "Client disconnected\n"
15+
def call(env)
16+
return invalid_connection_attempt unless Faye::WebSocket.websocket?( env )
17+
connection = Faye::WebSocket.new( env )
18+
19+
puts "Client #{connection} connected\n"
20+
@dispatcher.dispatch( 'client_connected', {}, connection )
21+
22+
23+
connection.onmessage = lambda do |event|
24+
@dispatcher.receive( event.data, connection )
25+
end
26+
27+
connection.onerror = lambda do |event|
28+
@dispatcher.dispatch( 'client_error', {}, connection )
29+
connection.onclose
30+
end
31+
32+
connection.onclose = lambda do |event|
33+
@dispatcher.dispatch( 'client_disconnected', {}, connection )
34+
connections.delete( connection )
35+
36+
puts "Client #{connection} disconnected\n"
37+
connection = nil
38+
end
39+
40+
connections << connection
41+
connection.rack_response
2842
end
2943

30-
def send_message(msg,uid)
31-
send_data msg, uid
32-
end
3344

34-
def broadcast_message(msg)
35-
send_data_all msg
45+
def broadcast_message(message)
46+
@connections.map do |connection|
47+
connection.send message
48+
end
3649
end
50+
51+
private
52+
53+
def invalid_connection_attempt
54+
[400,{'Content-Type' => 'text/plain'}, ['Connection was not a valid WebSocket connection']]
55+
end
56+
3757
end
3858
end

lib/websocket_rails/dispatcher.rb

+24-47
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,50 @@
11
require 'json'
22

33
module WebsocketRails
4-
class Dispatcher
5-
def initialize(connection)
6-
puts "Initializing dispatcher\n"
7-
@connection = connection
8-
@events = Hash.new {|h,k| h[k] = Array.new}
9-
@classes = Hash.new
10-
evaluate(@@event_routes) if @@event_routes
4+
class Dispatcher
5+
6+
def self.describe_events(&block)
7+
raise "This method has been deprecated. Please use WebsocketRails::Events.describe_events instead."
8+
end
9+
10+
attr_reader :events
11+
12+
def initialize(connection_manager)
13+
@connection_manager = connection_manager
14+
@events = Events.new( self )
1115
end
1216

13-
def receive(enc_message,env)
17+
def receive(enc_message,connection)
1418
message = JSON.parse( enc_message )
1519
event_name = message.first
1620
data = message.last
1721
data['received'] = Time.now.strftime("%I:%M:%p")
18-
dispatch( event_name, data, env )
22+
dispatch( event_name, data, connection )
1923
end
2024

21-
def send_message(event_name,data)
22-
@connection.send_message encoded_message( event_name, data.last ), data.first
25+
def send_message(event_name,data,connection)
26+
connection.send encoded_message( event_name, data )
2327
end
2428

2529
def broadcast_message(event_name,data)
26-
@connection.broadcast_message encoded_message( event_name, data )
30+
@connection_manager.broadcast_message encoded_message( event_name, data )
2731
end
2832

29-
def dispatch(event_name,data,env)
30-
puts "#{event_name} is handled by #{@events[event_name.to_sym].inspect}\n\n"
33+
def dispatch(event_name,message,connection)
3134
Fiber.new {
3235
event_symbol = event_name.to_sym
33-
message = [env['websocket.client_id'],data]
34-
@events[event_symbol].each do |event|
35-
method = event.last
36-
handler = event.first
37-
klass = @classes[handler]
38-
klass.instance_variable_set(:@_message,message)
39-
klass.send :execute_observers, event_symbol
40-
klass.send method if klass.respond_to?(method)
36+
events.routes_for(event_symbol) do |controller,method|
37+
controller.instance_variable_set(:@_message,message)
38+
controller.instance_variable_set(:@_connection,connection)
39+
controller.send :execute_observers, event_symbol
40+
controller.send method if controller.respond_to?(method)
4141
end
4242
}.resume
4343
end
44-
45-
def close_connection
46-
@connection.close_connection
47-
end
48-
44+
4945
def encoded_message(event_name,data)
5046
[event_name, data].to_json
5147
end
52-
53-
def subscribe(event_name,options)
54-
klass = options[:to] || raise("Must specify a class for to: option in event route")
55-
method = options[:with_method] || raise("Must specify a method for with_method: option in event route")
56-
controller = klass.new
57-
if @classes[klass].nil?
58-
@classes[klass] = controller
59-
controller.instance_variable_set(:@_dispatcher,self)
60-
controller.send :initialize_session if controller.respond_to?(:initialize_session)
61-
end
62-
@events[event_name] << [klass,method]
63-
end
64-
65-
def self.describe_events(&block)
66-
@@event_routes = block
67-
end
68-
69-
def evaluate(block)
70-
instance_eval &block
71-
end
48+
7249
end
7350
end

0 commit comments

Comments
 (0)