websocket-rails 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +22 -0
- data/Gemfile +4 -0
- data/README.md +66 -159
- data/Rakefile +31 -4
- data/bin/thin-socketrails +16 -1
- data/lib/assets/javascripts/websocket_rails/channel.js.coffee +23 -8
- data/lib/assets/javascripts/websocket_rails/event.js.coffee +40 -0
- data/lib/assets/javascripts/websocket_rails/http_connection.js.coffee +18 -10
- data/lib/assets/javascripts/websocket_rails/main.js +1 -0
- data/lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee +15 -10
- data/lib/assets/javascripts/websocket_rails/websocket_rails.js.coffee +41 -23
- data/lib/websocket-rails.rb +4 -4
- data/lib/websocket_rails/base_controller.rb +61 -29
- data/lib/websocket_rails/channel.rb +14 -5
- data/lib/websocket_rails/channel_manager.rb +3 -1
- data/lib/websocket_rails/connection_adapters.rb +34 -12
- data/lib/websocket_rails/connection_manager.rb +4 -0
- data/lib/websocket_rails/dispatcher.rb +27 -3
- data/lib/websocket_rails/engine.rb +2 -5
- data/lib/websocket_rails/event.rb +87 -42
- data/lib/websocket_rails/event_map.rb +70 -20
- data/lib/websocket_rails/event_queue.rb +4 -0
- data/lib/websocket_rails/internal_events.rb +21 -3
- data/lib/websocket_rails/logging.rb +18 -0
- data/lib/websocket_rails/version.rb +1 -1
- data/spec/dummy/log/test.log +0 -429
- data/spec/integration/connection_manager_spec.rb +3 -5
- data/spec/javascripts/generated/assets/channel.js +98 -0
- data/spec/javascripts/generated/assets/event.js +78 -0
- data/spec/javascripts/generated/assets/http_connection.js +108 -0
- data/spec/javascripts/generated/assets/websocket_connection.js +66 -0
- data/spec/javascripts/generated/assets/websocket_rails.js +180 -0
- data/spec/javascripts/generated/specs/channel_spec.js +66 -0
- data/spec/javascripts/generated/specs/event_spec.js +107 -0
- data/spec/javascripts/generated/specs/websocket_connection_spec.js +117 -0
- data/spec/javascripts/generated/specs/websocket_rails_spec.js +232 -0
- data/spec/javascripts/support/jasmine.yml +44 -0
- data/spec/javascripts/support/jasmine_config.rb +63 -0
- data/spec/javascripts/support/vendor/sinon-1.3.4.js +3555 -0
- data/spec/javascripts/websocket_rails/channel_spec.coffee +51 -0
- data/spec/javascripts/websocket_rails/event_spec.coffee +69 -0
- data/spec/javascripts/websocket_rails/websocket_connection_spec.coffee +86 -0
- data/spec/javascripts/websocket_rails/websocket_rails_spec.coffee +166 -0
- data/spec/support/helper_methods.rb +10 -1
- data/spec/unit/channel_spec.rb +28 -4
- data/spec/unit/connection_adapters_spec.rb +17 -0
- data/spec/unit/connection_manager_spec.rb +1 -1
- data/spec/unit/dispatcher_spec.rb +1 -1
- data/spec/unit/event_spec.rb +15 -11
- metadata +22 -4
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
# WebsocketRails Change Log
|
2
2
|
|
3
|
+
## Version 0.1.6
|
4
|
+
|
5
|
+
July 17 2012
|
6
|
+
|
7
|
+
* Added private channel support - Thanks to @MhdSyrwan
|
8
|
+
* Added DSL method for marking channels as private.
|
9
|
+
* Added support for attaching success and failure callbacks to triggered
|
10
|
+
events on the JavaScript client.
|
11
|
+
* Fixed JSON parsing bug in HTTP streaming client when multiple events
|
12
|
+
were received together.
|
13
|
+
* Added connection keepalive ping/pong timers to ensure clients do not
|
14
|
+
disconnect automatically. Ensures HTTP streaming works well on Heroku.
|
15
|
+
* Removed the requirement of using the thin-socketrails executable. The
|
16
|
+
executable will be removed entirely in the next release.
|
17
|
+
* Added Jasmine specs for CoffeeScript client.
|
18
|
+
* Exceptions triggered in controller actions are now serialized and
|
19
|
+
passed to the failure callback on the client that triggered the
|
20
|
+
action.
|
21
|
+
* Events triggered on the client before the connection is fully
|
22
|
+
established are now queued and sent in bulk once the connection is
|
23
|
+
ready.
|
24
|
+
|
3
25
|
## Version 0.1.5
|
4
26
|
|
5
27
|
July 3 2012
|
data/Gemfile
CHANGED
@@ -3,6 +3,9 @@ source "http://rubygems.org"
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
gem "rspec-rails"
|
6
|
+
gem "jasmine"
|
7
|
+
gem "headless"
|
8
|
+
gem "coffee-script"
|
6
9
|
gem "thin"
|
7
10
|
gem "eventmachine", ">= 1.0.0.beta.3"
|
8
11
|
gem "faye-websocket"
|
@@ -10,6 +13,7 @@ gem "simplecov"
|
|
10
13
|
gem "ruby_gntp"
|
11
14
|
gem "guard"
|
12
15
|
gem "guard-rspec"
|
16
|
+
gem "guard-coffeescript"
|
13
17
|
|
14
18
|
platforms :jruby do
|
15
19
|
gem 'activerecord-jdbcsqlite3-adapter', :require => 'jdbc-sqlite3', :require => 'arjdbc'
|
data/README.md
CHANGED
@@ -4,199 +4,106 @@
|
|
4
4
|
|
5
5
|
If you haven't done so yet, check out the [Project Page](http://danknox.github.com/websocket-rails/) to get a feel for the project direction. Feedback is very much appreciated. Post an issue on the issue tracker or [shoot us an email](mailto://support@threedotloft.com) to give us your thoughts.
|
6
6
|
|
7
|
-
##
|
8
|
-
|
9
|
-
Plug and play WebSocket support for ruby on rails with streaming HTTP fallback for improved cross browser compatibility. 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.
|
10
|
-
|
11
|
-
*Important Note*
|
12
|
-
|
13
|
-
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.
|
14
|
-
|
15
|
-
*Update*
|
16
|
-
|
17
|
-
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.
|
18
|
-
|
19
|
-
## Installation
|
20
|
-
|
21
|
-
Check out the [Example Application](https://github.com/DanKnox/websocket-rails-Example-Project) for additional information.
|
22
|
-
|
23
|
-
1. Add the gem to your Gemfile
|
24
|
-
3. Create a WebsocketRails controller - [See Documentation](http://rdoc.info/github/DanKnox/websocket-rails/master/frames/WebsocketRails/BaseController)
|
25
|
-
4. Create an `events.rb` initializer file to map events to your controller - [See Documentation](http://rdoc.info/github/DanKnox/websocket-rails/master/frames/WebsocketRails/EventMap)
|
26
|
-
5. Launch the web server and connect a WebSocket client to `ws://yourserver:port/websocket`
|
27
|
-
|
28
|
-
*Important Note About Web Servers*
|
29
|
-
|
30
|
-
Thin is the only web server currently supported. Use the `thin-socketrails` executable to override the Thin connection timeout setting. The full command to start the server in development is `thin-socketrails -p 3000 start`. Be sure to enable config.threadsafe! in your rails application and use the Rack::Fiberpool middleware to take advantage of Thin's asynchronous request processing.
|
31
|
-
|
32
|
-
````ruby
|
33
|
-
gem 'websocket-rails'
|
34
|
-
````
|
7
|
+
## Update July 17 2012
|
35
8
|
|
36
|
-
|
9
|
+
We just released a new version containing significant new functionality.
|
10
|
+
We will be updating the documentation and wiki guides to cover it all over the next day
|
11
|
+
or two but check out the
|
12
|
+
[CHANGELOG](https://github.com/DanKnox/websocket-rails/blob/master/CHANGELOG.md) to get an early preview.
|
37
13
|
|
38
|
-
|
14
|
+
## Overview
|
39
15
|
|
40
|
-
|
16
|
+
Start treating client side events as first class citizens inside your
|
17
|
+
Rails application with a built in WebSocket server. Sure, WebSockets
|
18
|
+
aren't quite universal yet. That's why we also support streaming HTTP.
|
19
|
+
Oh, and if you don't mind running a separate process, you can support
|
20
|
+
just about any browser with Flash sockets.
|
41
21
|
|
42
|
-
|
22
|
+
## Respond Quicker with Socket Events
|
43
23
|
|
44
|
-
|
45
|
-
client as `namespace.event_name` will now be dispatched to the action
|
46
|
-
subscribed to the `event_name` event under the `namespace` namespace.
|
47
|
-
See the [EventMap
|
48
|
-
Documentation](http://rdoc.info/github/DanKnox/websocket-rails/master/frames/WebsocketRails/EventMap) for more details.
|
24
|
+
Map events to controller actions using an Event Router.
|
49
25
|
|
50
26
|
````ruby
|
51
|
-
# app/config/initializers
|
52
|
-
|
53
27
|
WebsocketRails::EventMap.describe do
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
# You can subscribe any number of controller actions to a single event
|
58
|
-
subscribe :new_message, to: ChatController, with_method: :new_message
|
59
|
-
subscribe :new_message, to: ChatLogController, with_method: :log_message
|
60
|
-
|
61
|
-
subscribe :new_user, to: ChatController, with_method: :new_user
|
62
|
-
subscribe :change_username, to: ChatController, with_method: :change_username
|
63
|
-
|
64
|
-
namespace :product do
|
65
|
-
subscribe :new, to: ProductController, with_method: :new_product
|
28
|
+
namespace :tasks do
|
29
|
+
subscribe :create, :to => TaskController, :with_method => :create
|
66
30
|
end
|
67
|
-
|
68
|
-
# The :client_disconnected method is fired automatically when a client disconnects
|
69
|
-
subscribe :client_disconnected, to: ChatController, with_method: :delete_user
|
70
31
|
end
|
71
32
|
````
|
72
33
|
|
73
|
-
|
74
|
-
|
75
|
-
## WebSocketRails JavaScript Client
|
76
|
-
|
77
|
-
There is an accompanying JavaScript client [located here](https://github.com/DanKnox/websocket-rails/tree/master/lib/assets/javascripts/websocket_rails). The client detects support for WebSockets in the browser and falls back to HTTP streaming if it is unavailable. The client currently works in every major browser except for internet explorer. If you are using the current master branch of this repository, you can require the client in your application.js manifest directly: `//= require websocket_rails/main`. The client will be released to rubygems soon.
|
34
|
+
Trigger events using our JavaScript client.
|
78
35
|
|
79
36
|
````javascript
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
var dispatcher = new WebSocketRails("localhost:port/websocket");
|
84
|
-
|
85
|
-
dispatcher.on_open = function() {
|
86
|
-
// trigger a server event immediately after opening connection
|
87
|
-
dispatcher.trigger('new_user',{user_name: 'guest'});
|
37
|
+
var task = {
|
38
|
+
name: 'Start taking advantage of WebSockets',
|
39
|
+
completed: false
|
88
40
|
}
|
89
|
-
|
90
|
-
|
91
|
-
dispatcher.trigger('event_name',object_to_be_serialized_to_json);
|
92
|
-
|
93
|
-
//Listening for new events from the server
|
94
|
-
dispatcher.bind('event_name', function(data) {
|
95
|
-
console.log(data.user_name);
|
96
|
-
})
|
41
|
+
dispatcher = new WebSocketRails('localhost:3000/websocket');
|
42
|
+
dispatcher.trigger('tasks.create', task);
|
97
43
|
````
|
98
44
|
|
99
|
-
|
100
|
-
|
101
|
-
## Controllers
|
102
|
-
|
103
|
-
There are a few differences between WebSocket controllers and standard Rails controllers. The biggest of which, is that each event will be handled by the same, continually running instance of your controller class. This means that if you set any instance variables in your methods, they will still be available when the next event is processed. On top of that, every single client that is connected will share these same instance variables. This can be an advantage if used properly, but it can also lead to bugs if not expected. We provide our own `DataStore` object accessible in a WebsocketRails controller to make it easier to store data isolated from each connected client. This is explained further below.
|
104
|
-
|
105
|
-
Do not override the `initialize` method in your class to set up. Instead, define an `initialize_session` method and perform your set up there. The `initialize_session` method will be called the first time a controller is subscribed to an event in the event router. Instance variables defined in the `initialize_session` method will be available throughout the course of the server lifetime.
|
106
|
-
|
107
|
-
````
|
108
|
-
class ChatController < WebsocketRails::BaseController
|
109
|
-
def initialize_session
|
110
|
-
# perform application setup here
|
111
|
-
@message_count = 0
|
112
|
-
end
|
113
|
-
end
|
114
|
-
````
|
115
|
-
|
116
|
-
The WebsocketRails::BaseController class provides methods for working with the WebSocket connection. Make sure you extend this class for controllers that you are using. The two most important methods are `send_message` and `broadcast_message`. The `send_message` method sends a message to the client that initiated this event, the `broadcast_message` method broadcasts messages to all connected clients. Both methods take two arguments, the event name to trigger on the client, and the message that accompanies it.
|
45
|
+
Handle events in your controller.
|
117
46
|
|
118
47
|
````ruby
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
def new_message
|
129
|
-
# Print the new message and client id to the console
|
130
|
-
puts "Message from client #{client_id} received: #{message.inspect}"
|
131
|
-
|
132
|
-
# Broadcast the new message to all connected clients
|
133
|
-
broadcast_message :new_message, message
|
48
|
+
class TaskController < WebsocketRails::BaseController
|
49
|
+
def create
|
50
|
+
# The `message` method contains the data received
|
51
|
+
task = Task.new message
|
52
|
+
if task.save
|
53
|
+
send_message :create_success, task, :namespace => :tasks
|
54
|
+
else
|
55
|
+
send_message :create_fail, task, :namespace => :tasks
|
56
|
+
end
|
134
57
|
end
|
135
58
|
end
|
136
59
|
````
|
137
60
|
|
138
|
-
|
139
|
-
|
140
|
-
The first method used is the `client_id` method. This method contains the ID of the current WebSocket client that initiated this event. Each connected client is randomly assigned an ID upon connecting to the server. You can keep track of who is who by storing the `client_id` associated with each user somewhere. You can also use the provided `DataStore` (explained later) to make keeping track of users easier.
|
61
|
+
Receive the response in the client.
|
141
62
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
## Data Store
|
147
|
-
|
148
|
-
The `DataStore` object is a private Hash. When you access it in a controller, you will be accessing a Hash that is private to the client that initiated the event currently executing. You can use it exactly as you would a regular Hash, except you do not have to worry about it being overridden by the next client that triggers an event. This means that unlike instance variables in WebsocketRails controllers which are shared amongst all connected clients, the data store is an easy place to temporarily persist data for each user between events.
|
149
|
-
|
150
|
-
You can access the `DataStore` object by using the `data_store` controller method.
|
151
|
-
|
152
|
-
````ruby
|
153
|
-
class ChatController < WebsocketRails::BaseController
|
154
|
-
def new_user
|
155
|
-
# The instance variable would be overwritten when the next user joins
|
156
|
-
@user = User.new(name: message[:user_name]) # No Good!
|
157
|
-
|
158
|
-
# This will be private for each user
|
159
|
-
data_store[:user] = User.new(name: message[:user_name]) # Good!
|
160
|
-
broadcast_user_list
|
161
|
-
end
|
162
|
-
end
|
63
|
+
````javascript
|
64
|
+
dispatcher.bind('tasks.create_successful', function(task) {
|
65
|
+
console.log('successfully created ' + task.name);
|
66
|
+
});
|
163
67
|
````
|
164
68
|
|
165
|
-
|
166
|
-
|
167
|
-
Given our ongoing chat server example, we could collect all of the current `User` objects like so:
|
168
|
-
d
|
169
|
-
````ruby
|
170
|
-
data_store[:user] = 'User3'
|
171
|
-
data_store.each_user
|
172
|
-
=> ['User1','User2','User3']
|
173
|
-
````
|
69
|
+
## Channel Support
|
174
70
|
|
175
|
-
|
71
|
+
Keep your users up to date without waiting for them to refresh the page.
|
72
|
+
Subscribe them to a channel and update it from wherever you please.
|
176
73
|
|
177
|
-
|
178
|
-
def broadcast_user_list
|
179
|
-
users = data_store.each_user
|
180
|
-
broadcast_message :user_list, users
|
181
|
-
end
|
182
|
-
````
|
74
|
+
Tune in on the client side.
|
183
75
|
|
184
|
-
|
185
|
-
|
186
|
-
|
76
|
+
````javascript
|
77
|
+
channel = dispatcher.subscribe('posts');
|
78
|
+
channel.bind('new', function(post) {
|
79
|
+
console.log('a new post about '+post.title+' arrived!');
|
80
|
+
});
|
81
|
+
````
|
187
82
|
|
188
|
-
|
83
|
+
Broadcast to the channel from anywhere inside your Rails application. An
|
84
|
+
existing controller, a model, a background job, or a new WebsocketRails
|
85
|
+
controller.
|
189
86
|
|
190
87
|
````ruby
|
191
|
-
|
192
|
-
|
88
|
+
latest_post = Post.latest
|
89
|
+
WebsocketRails[:posts].trigger('new', latest_post)
|
193
90
|
````
|
194
91
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
92
|
+
## Installation and Usage Guides
|
93
|
+
|
94
|
+
Check out the [Example Application](https://github.com/DanKnox/websocket-rails-Example-Project) for an example implementation.
|
95
|
+
|
96
|
+
* [Installation
|
97
|
+
Guide](https://github.com/DanKnox/websocket-rails/wiki/Installation-and-Setup)
|
98
|
+
* [Event
|
99
|
+
Router](https://github.com/DanKnox/websocket-rails/wiki/The-Event-Router)
|
100
|
+
* [WebsocketRails Controllers](https://github.com/DanKnox/websocket-rails/wiki/WebsocketRails Controllers)
|
101
|
+
* [Using the JavaScript
|
102
|
+
Client](https://github.com/DanKnox/websocket-rails/wiki/Using-the-JavaScript-Client)
|
103
|
+
* [Using
|
104
|
+
Channels](https://github.com/DanKnox/websocket-rails/wiki/Working-with-Channels)
|
105
|
+
* [The
|
106
|
+
DataStore](https://github.com/DanKnox/websocket-rails/wiki/Using-the-DataStore)
|
200
107
|
|
201
108
|
## Development
|
202
109
|
|
data/Rakefile
CHANGED
@@ -17,8 +17,6 @@ end
|
|
17
17
|
|
18
18
|
Bundler::GemHelper.install_tasks
|
19
19
|
|
20
|
-
task :default => :spec
|
21
|
-
|
22
20
|
RDoc::Task.new(:rdoc) do |rdoc|
|
23
21
|
rdoc.rdoc_dir = 'rdoc'
|
24
22
|
rdoc.title = 'websocket-rails'
|
@@ -29,17 +27,46 @@ end
|
|
29
27
|
|
30
28
|
require 'rspec/core/rake_task'
|
31
29
|
|
32
|
-
desc 'Default: run specs.'
|
33
|
-
task :default => :
|
30
|
+
desc 'Default: run RSpec and Jasmine specs.'
|
31
|
+
task :default => :spec_and_jasmine
|
34
32
|
|
35
33
|
desc "Run specs"
|
36
34
|
RSpec::Core::RakeTask.new do |t|
|
37
35
|
t.pattern = "./spec/**/*_spec.rb"
|
38
36
|
end
|
39
37
|
|
38
|
+
desc "Run rspec and jasmine:ci at the same time"
|
39
|
+
task :spec_and_jasmine do
|
40
|
+
Rake::Task["spec"].execute
|
41
|
+
Rake::Task["jasmine:ci:headless"].execute
|
42
|
+
end
|
43
|
+
|
40
44
|
desc "Generate code coverage"
|
41
45
|
task :coverage do
|
42
46
|
ENV['COVERAGE'] = 'true'
|
43
47
|
Rake::Task["spec"].execute
|
44
48
|
`open coverage/index.html`
|
45
49
|
end
|
50
|
+
|
51
|
+
begin
|
52
|
+
require 'jasmine'
|
53
|
+
load 'jasmine/tasks/jasmine.rake'
|
54
|
+
rescue LoadError
|
55
|
+
task :jasmine do
|
56
|
+
abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
require 'headless'
|
61
|
+
require 'selenium-webdriver'
|
62
|
+
|
63
|
+
namespace :jasmine do
|
64
|
+
namespace :ci do
|
65
|
+
desc "Run Jasmine CI build headlessly"
|
66
|
+
task :headless do
|
67
|
+
ENV['DISPLAY'] = ':99.0'
|
68
|
+
puts "Running Jasmine Headlessly"
|
69
|
+
Rake::Task['jasmine:ci'].invoke
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/bin/thin-socketrails
CHANGED
@@ -18,6 +18,21 @@
|
|
18
18
|
require 'rubygems'
|
19
19
|
require 'thin'
|
20
20
|
|
21
|
+
puts <<END
|
22
|
+
*** Deprecation Notice***
|
23
|
+
|
24
|
+
The thin-socketrails executable is now deprecated and
|
25
|
+
will be removed in the next release.
|
26
|
+
|
27
|
+
You may use the regular thin executable to launch the
|
28
|
+
server now.
|
29
|
+
|
30
|
+
Other EventMachine based web servers should now be
|
31
|
+
supported but have not yet been tested.
|
32
|
+
|
33
|
+
*************************
|
34
|
+
END
|
35
|
+
|
21
36
|
if EM::VERSION < "1.0.0"
|
22
37
|
begin
|
23
38
|
old_verbose, $VERBOSE = $VERBOSE, nil
|
@@ -27,4 +42,4 @@ if EM::VERSION < "1.0.0"
|
|
27
42
|
end
|
28
43
|
end
|
29
44
|
|
30
|
-
Thin::Runner.new(ARGV).run!
|
45
|
+
Thin::Runner.new(ARGV).run!
|
@@ -9,18 +9,33 @@ For instance:
|
|
9
9
|
###
|
10
10
|
class WebSocketRails.Channel
|
11
11
|
|
12
|
-
constructor: (@name,@
|
13
|
-
|
14
|
-
|
12
|
+
constructor: (@name,@_dispatcher,@is_private) ->
|
13
|
+
if @is_private
|
14
|
+
event_name = 'websocket_rails.subscribe_private'
|
15
|
+
else
|
16
|
+
event_name = 'websocket_rails.subscribe'
|
17
|
+
|
18
|
+
event = new WebSocketRails.Event( [event_name, {data: {channel: @name}},@_dispatcher.connection_id], @_success_launcher, @_failure_launcher)
|
19
|
+
@_dispatcher.trigger_event event
|
20
|
+
@_callbacks = {}
|
15
21
|
|
16
22
|
bind: (event_name, callback) =>
|
17
|
-
@
|
18
|
-
@
|
23
|
+
@_callbacks[event_name] ?= []
|
24
|
+
@_callbacks[event_name].push callback
|
19
25
|
|
20
26
|
trigger: (event_name, message) =>
|
21
|
-
|
27
|
+
event = new WebSocketRails.Event( [event_name, {channel: @name, data: message}, @_dispatcher.connection_id] )
|
28
|
+
@_dispatcher.trigger_event event
|
22
29
|
|
23
30
|
dispatch: (event_name, message) =>
|
24
|
-
return unless @
|
25
|
-
for callback in @
|
31
|
+
return unless @_callbacks[event_name]?
|
32
|
+
for callback in @_callbacks[event_name]
|
26
33
|
callback message
|
34
|
+
|
35
|
+
# using this method because @on_success will not be defined when the constructor is executed
|
36
|
+
_success_launcher: (data) =>
|
37
|
+
@on_success(data) if @on_success?
|
38
|
+
|
39
|
+
# using this method because @on_failure will not be defined when the constructor is executed
|
40
|
+
_failure_launcher: (data) =>
|
41
|
+
@on_failure(data) if @on_failure?
|
@@ -0,0 +1,40 @@
|
|
1
|
+
###
|
2
|
+
The Event object stores all the relevant event information.
|
3
|
+
###
|
4
|
+
|
5
|
+
class WebSocketRails.Event
|
6
|
+
|
7
|
+
constructor: (data,@success_callback,@failure_callback) ->
|
8
|
+
@name = data[0]
|
9
|
+
attr = data[1]
|
10
|
+
if attr?
|
11
|
+
@id = if attr['id']? then attr['id'] else (((1+Math.random())*0x10000)|0)
|
12
|
+
@channel = if attr.channel? then attr.channel
|
13
|
+
@data = if attr.data? then attr.data else attr
|
14
|
+
@connection_id = data[2]
|
15
|
+
if attr.success?
|
16
|
+
@result = true
|
17
|
+
@success = attr.success
|
18
|
+
|
19
|
+
is_channel: =>
|
20
|
+
@channel?
|
21
|
+
|
22
|
+
is_result: =>
|
23
|
+
@result == true
|
24
|
+
|
25
|
+
is_ping: =>
|
26
|
+
@name == 'websocket_rails.ping'
|
27
|
+
|
28
|
+
serialize: =>
|
29
|
+
JSON.stringify [@name, @attributes()]
|
30
|
+
|
31
|
+
attributes: =>
|
32
|
+
id: @id,
|
33
|
+
channel: @channel,
|
34
|
+
data: @data
|
35
|
+
|
36
|
+
run_callbacks: (success,data) =>
|
37
|
+
if success == true
|
38
|
+
@success_callback?(data)
|
39
|
+
else
|
40
|
+
@failure_callback?(data)
|
@@ -21,8 +21,9 @@ class WebSocketRails.HttpConnection
|
|
21
21
|
xmlhttp
|
22
22
|
|
23
23
|
constructor: (@url, @dispatcher) ->
|
24
|
-
@_conn
|
25
|
-
@last_pos
|
24
|
+
@_conn = @createXMLHttpObject()
|
25
|
+
@last_pos = 0
|
26
|
+
@message_queue = []
|
26
27
|
@_conn.onreadystatechange = @parse_stream
|
27
28
|
@_conn.open "GET", "/websocket", true
|
28
29
|
@_conn.send()
|
@@ -31,16 +32,15 @@ class WebSocketRails.HttpConnection
|
|
31
32
|
if @_conn.readyState == 3
|
32
33
|
data = @_conn.responseText.substring @last_pos
|
33
34
|
@last_pos = @_conn.responseText.length
|
35
|
+
data = data.replace "]][[", "],["
|
34
36
|
decoded_data = JSON.parse data
|
35
37
|
@dispatcher.new_message decoded_data
|
36
38
|
|
37
|
-
trigger: (
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
payload = JSON.stringify [channel_name, event_name, data]
|
43
|
-
@post_data connection_id, payload
|
39
|
+
trigger: (event) =>
|
40
|
+
if @dispatcher.state != 'connected'
|
41
|
+
@message_queue.push event
|
42
|
+
else
|
43
|
+
@post_data @dispatcher.connection_id, event.serialize()
|
44
44
|
|
45
45
|
post_data: (connection_id, payload) ->
|
46
46
|
$.ajax "/websocket",
|
@@ -49,4 +49,12 @@ class WebSocketRails.HttpConnection
|
|
49
49
|
client_id: connection_id
|
50
50
|
data: payload
|
51
51
|
success: ->
|
52
|
-
|
52
|
+
|
53
|
+
flush_queue: (connection_id) =>
|
54
|
+
for event in @message_queue
|
55
|
+
# Events queued before connecting do not have the correct
|
56
|
+
# connection_id set yet. We need to update it before dispatching.
|
57
|
+
if connection_id?
|
58
|
+
event.connection_id = @dispatcher.connection_id
|
59
|
+
@trigger event
|
60
|
+
@message_queue = []
|
@@ -4,22 +4,27 @@ WebSocket Interface for the WebSocketRails client.
|
|
4
4
|
class WebSocketRails.WebSocketConnection
|
5
5
|
|
6
6
|
constructor: (@url,@dispatcher) ->
|
7
|
-
@
|
7
|
+
@url = "ws://#{@url}" unless @url.match(/^wss?:\/\//)
|
8
|
+
@message_queue = []
|
9
|
+
@_conn = new WebSocket(@url)
|
8
10
|
@_conn.onmessage = @on_message
|
9
11
|
@_conn.onclose = @on_close
|
10
12
|
|
11
|
-
trigger: (
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
payload = JSON.stringify [channel_name, event_name, data]
|
17
|
-
@_conn.send payload
|
13
|
+
trigger: (event) =>
|
14
|
+
if @dispatcher.state != 'connected'
|
15
|
+
@message_queue.push event
|
16
|
+
else
|
17
|
+
@_conn.send event.serialize()
|
18
18
|
|
19
19
|
on_message: (event) =>
|
20
20
|
data = JSON.parse event.data
|
21
|
-
console.log data
|
22
21
|
@dispatcher.new_message data
|
23
22
|
|
24
23
|
on_close: (event) =>
|
25
|
-
|
24
|
+
close_event = new WebSocketRails.Event(['connection_closed',{}])
|
25
|
+
@dispatcher.dispatch close_event
|
26
|
+
|
27
|
+
flush_queue: =>
|
28
|
+
for event in @message_queue
|
29
|
+
@_conn.send event.serialize()
|
30
|
+
@message_queue = []
|