websocket-rails 0.4.9 → 0.5.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 +18 -0
- data/README.md +1 -1
- data/lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee +7 -1
- data/lib/generators/websocket_rails/install/install_generator.rb +2 -1
- data/lib/generators/websocket_rails/install/templates/events.rb +0 -36
- data/lib/generators/websocket_rails/install/templates/websocket_rails.rb +41 -0
- data/lib/rails/tasks/websocket_rails.tasks +5 -3
- data/lib/websocket-rails.rb +14 -0
- data/lib/websocket_rails/base_controller.rb +31 -43
- data/lib/websocket_rails/channel_manager.rb +1 -3
- data/lib/websocket_rails/configuration.rb +8 -0
- data/lib/websocket_rails/connection_adapters.rb +29 -4
- data/lib/websocket_rails/connection_manager.rb +4 -0
- data/lib/websocket_rails/controller_factory.rb +9 -4
- data/lib/websocket_rails/data_store.rb +5 -5
- data/lib/websocket_rails/dispatcher.rb +13 -8
- data/lib/websocket_rails/event.rb +8 -4
- data/lib/websocket_rails/synchronization.rb +25 -10
- data/lib/websocket_rails/user_manager.rb +85 -0
- data/lib/websocket_rails/version.rb +1 -1
- data/spec/dummy/app/controllers/chat_controller.rb +14 -14
- data/spec/javascripts/generated/assets/websocket_connection.js +5 -1
- data/spec/unit/connection_adapters_spec.rb +45 -1
- data/spec/unit/controller_factory_spec.rb +6 -6
- data/spec/unit/dispatcher_spec.rb +2 -12
- data/spec/unit/event_spec.rb +8 -1
- data/spec/unit/synchronization_spec.rb +44 -25
- data/spec/unit/user_manager_spec.rb +61 -0
- metadata +8 -5
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# WebsocketRails Change Log
|
2
2
|
|
3
|
+
## Version 0.5.0
|
4
|
+
|
5
|
+
September 2 2013
|
6
|
+
|
7
|
+
* Use window.location.protocol to choose between ws:// and wss://
|
8
|
+
shcheme. - Thanks to @depili
|
9
|
+
* Override ConnectionManager#inspect to clean up the output from `rake
|
10
|
+
routes`
|
11
|
+
* Add a basic Global UserManager for triggering events on specific users
|
12
|
+
from anywhere inside your app without creating a dedicated user channel.
|
13
|
+
* Deprecate the old controller observer system and implement full Rails
|
14
|
+
AbstractController::Callbacks support. - Thanks to @pitr
|
15
|
+
* Reload the events.rb event route file each time an event is fired. -
|
16
|
+
Thanks to @moaa
|
17
|
+
* Separate the event route file and WebsocketRails configuration files.
|
18
|
+
The events.rb now lives in `config/events.rb`. The configuration should
|
19
|
+
remain in an initializer located at `config/initializers/websocket_rails.rb`. - Thanks to @moaa
|
20
|
+
|
3
21
|
## Version 0.4.9
|
4
22
|
|
5
23
|
July 9 2013
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ If you haven't done so yet, check out the [Project Page](http://danknox.github.c
|
|
6
6
|
|
7
7
|
**Find us on IRC #websocket-rails**
|
8
8
|
|
9
|
-
Stop by #websocket-rails if you would like to chat or have any
|
9
|
+
Stop by #websocket-rails on freenode if you would like to chat or have any
|
10
10
|
questions.
|
11
11
|
|
12
12
|
## Recent Updates
|
@@ -4,7 +4,13 @@ WebSocket Interface for the WebSocketRails client.
|
|
4
4
|
class WebSocketRails.WebSocketConnection
|
5
5
|
|
6
6
|
constructor: (@url,@dispatcher) ->
|
7
|
-
|
7
|
+
if @url.match(/^wss?:\/\//)
|
8
|
+
console.log "WARNING: Using connection urls with protocol specified is depricated"
|
9
|
+
else if window.location.protocol == 'http:'
|
10
|
+
@url = "ws://#{@url}"
|
11
|
+
else
|
12
|
+
@url = "wss://#{@url}"
|
13
|
+
|
8
14
|
@message_queue = []
|
9
15
|
@_conn = new WebSocket(@url)
|
10
16
|
@_conn.onmessage = @on_message
|
@@ -11,7 +11,8 @@ module WebsocketRails
|
|
11
11
|
:desc => "Javascript manifest file to modify (or create)"
|
12
12
|
|
13
13
|
def create_events_initializer_file
|
14
|
-
template 'events.rb', File.join('config', '
|
14
|
+
template 'events.rb', File.join('config', 'events.rb')
|
15
|
+
template 'websocket_rails.rb', File.join('config', 'initializers', 'websocket_rails.rb')
|
15
16
|
end
|
16
17
|
|
17
18
|
def inject_websocket_rails_client
|
@@ -1,39 +1,3 @@
|
|
1
|
-
WebsocketRails.setup do |config|
|
2
|
-
|
3
|
-
# Uncomment to override the default log level. The log level can be
|
4
|
-
# any of the standard Logger log levels. By default it will mirror the
|
5
|
-
# current Rails environment log level.
|
6
|
-
# config.log_level = :debug
|
7
|
-
|
8
|
-
# Uncomment to change the default log file path.
|
9
|
-
# config.log_path = "#{Rails.root}/log/websocket_rails.log"
|
10
|
-
|
11
|
-
# Set to true if you wish to log the internal websocket_rails events
|
12
|
-
# such as the keepalive `websocket_rails.ping` event.
|
13
|
-
# config.log_internal_events = false
|
14
|
-
|
15
|
-
# Change to true to enable standalone server mode
|
16
|
-
# Start the standalone server with rake websocket_rails:start_server
|
17
|
-
# * Requires Redis
|
18
|
-
config.standalone = false
|
19
|
-
|
20
|
-
# Change to true to enable channel synchronization between
|
21
|
-
# multiple server instances.
|
22
|
-
# * Requires Redis.
|
23
|
-
config.synchronize = false
|
24
|
-
|
25
|
-
# Uncomment and edit to point to a different redis instance.
|
26
|
-
# Will not be used unless standalone or synchronization mode
|
27
|
-
# is enabled.
|
28
|
-
# config.redis_options = {:host => 'localhost', :port => '6379'}
|
29
|
-
|
30
|
-
# By default, all subscribers in to a channel will be removed
|
31
|
-
# when that channel is made private. If you don't wish active
|
32
|
-
# subscribers to be removed from a previously public channel
|
33
|
-
# when making it private, set the following to true.
|
34
|
-
# config.keep_subscribers_when_private = false
|
35
|
-
end
|
36
|
-
|
37
1
|
WebsocketRails::EventMap.describe do
|
38
2
|
# You can use this file to map incoming events to controller actions.
|
39
3
|
# One event can be mapped to any number of controller actions. The
|
@@ -0,0 +1,41 @@
|
|
1
|
+
WebsocketRails.setup do |config|
|
2
|
+
|
3
|
+
# Uncomment to override the default log level. The log level can be
|
4
|
+
# any of the standard Logger log levels. By default it will mirror the
|
5
|
+
# current Rails environment log level.
|
6
|
+
# config.log_level = :debug
|
7
|
+
|
8
|
+
# Uncomment to change the default log file path.
|
9
|
+
# config.log_path = "#{Rails.root}/log/websocket_rails.log"
|
10
|
+
|
11
|
+
# Set to true if you wish to log the internal websocket_rails events
|
12
|
+
# such as the keepalive `websocket_rails.ping` event.
|
13
|
+
# config.log_internal_events = false
|
14
|
+
|
15
|
+
# Change to true to enable standalone server mode
|
16
|
+
# Start the standalone server with rake websocket_rails:start_server
|
17
|
+
# * Requires Redis
|
18
|
+
config.standalone = false
|
19
|
+
|
20
|
+
# Change to true to enable channel synchronization between
|
21
|
+
# multiple server instances.
|
22
|
+
# * Requires Redis.
|
23
|
+
config.synchronize = false
|
24
|
+
|
25
|
+
# Uncomment and edit to point to a different redis instance.
|
26
|
+
# Will not be used unless standalone or synchronization mode
|
27
|
+
# is enabled.
|
28
|
+
# config.redis_options = {:host => 'localhost', :port => '6379'}
|
29
|
+
|
30
|
+
# By default, all subscribers in to a channel will be removed
|
31
|
+
# when that channel is made private. If you don't wish active
|
32
|
+
# subscribers to be removed from a previously public channel
|
33
|
+
# when making it private, set the following to true.
|
34
|
+
# config.keep_subscribers_when_private = false
|
35
|
+
|
36
|
+
# Used as the key for the WebsocketRails.users Hash. This method
|
37
|
+
# will be called on the `current_user` object in your controller
|
38
|
+
# if one exists. If `current_user` does not exist or does not
|
39
|
+
# respond to the identifier, the key will default to `connection.id`
|
40
|
+
# config.user_identifier = :id
|
41
|
+
end
|
@@ -2,7 +2,8 @@ namespace :websocket_rails do
|
|
2
2
|
desc 'Start the WebsocketRails standalone server.'
|
3
3
|
task :start_server do
|
4
4
|
require "thin"
|
5
|
-
load "#{Rails.root}/config/initializers/
|
5
|
+
load "#{Rails.root}/config/initializers/websocket_rails.rb"
|
6
|
+
load "#{Rails.root}/config/events.rb"
|
6
7
|
|
7
8
|
options = WebsocketRails.config.thin_options
|
8
9
|
|
@@ -18,7 +19,8 @@ namespace :websocket_rails do
|
|
18
19
|
desc 'Stop the WebsocketRails standalone server.'
|
19
20
|
task :stop_server do
|
20
21
|
require "thin"
|
21
|
-
load "#{Rails.root}/config/initializers/
|
22
|
+
load "#{Rails.root}/config/initializers/websocket_rails.rb"
|
23
|
+
load "#{Rails.root}/config/events.rb"
|
22
24
|
|
23
25
|
options = WebsocketRails.config.thin_options
|
24
26
|
|
@@ -31,6 +33,6 @@ end
|
|
31
33
|
def warn_if_standalone_not_enabled!
|
32
34
|
return if WebsocketRails.standalone?
|
33
35
|
puts "Fail!"
|
34
|
-
puts "You must enable standalone mode in your
|
36
|
+
puts "You must enable standalone mode in your websocket_rails.rb initializer to use the standalone server."
|
35
37
|
exit 1
|
36
38
|
end
|
data/lib/websocket-rails.rb
CHANGED
@@ -41,6 +41,7 @@ require 'websocket_rails/event_map'
|
|
41
41
|
require 'websocket_rails/event_queue'
|
42
42
|
require 'websocket_rails/channel'
|
43
43
|
require 'websocket_rails/channel_manager'
|
44
|
+
require 'websocket_rails/user_manager'
|
44
45
|
require 'websocket_rails/base_controller'
|
45
46
|
require 'websocket_rails/internal_events'
|
46
47
|
|
@@ -48,6 +49,7 @@ require 'websocket_rails/connection_adapters'
|
|
48
49
|
require 'websocket_rails/connection_adapters/http'
|
49
50
|
require 'websocket_rails/connection_adapters/web_socket'
|
50
51
|
|
52
|
+
load "#{Rails.root}/config/events.rb" if File.exists?("#{Rails.root}/config/events.rb")
|
51
53
|
|
52
54
|
# Exceptions
|
53
55
|
class WebsocketRails::InvalidConnectionError < StandardError
|
@@ -83,6 +85,18 @@ class WebsocketRails::EventRoutingError < StandardError
|
|
83
85
|
|
84
86
|
end
|
85
87
|
|
88
|
+
class WebsocketRails::ConfigDeprecationError < StandardError
|
89
|
+
def to_s
|
90
|
+
out = "Deprecation Error:\n\n\t"
|
91
|
+
out << "config/initializers/events.rb has been moved to config/events.rb\n\t"
|
92
|
+
out << "Make sure events.rb is in the proper location and the old one has been removed.\n\t"
|
93
|
+
out << "More information can be found in the wiki.\n\n"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
raise WebsocketRails::ConfigDeprecationError if File.exists?("config/initializers/events.rb")
|
98
|
+
|
99
|
+
|
86
100
|
# Deprecation Notices
|
87
101
|
class WebsocketRails::Dispatcher
|
88
102
|
def self.describe_events(&block)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "websocket_rails/data_store"
|
2
|
+
require 'abstract_controller/callbacks'
|
2
3
|
|
3
4
|
module WebsocketRails
|
4
5
|
# Provides controller helper methods for developing a WebsocketRails controller. Action methods
|
@@ -18,6 +19,24 @@ module WebsocketRails
|
|
18
19
|
#
|
19
20
|
class BaseController
|
20
21
|
|
22
|
+
# We need process_action to be in a module loaded before AbstractController::Callbacks
|
23
|
+
# to get inheritance properly
|
24
|
+
module Metal
|
25
|
+
def process_action(method, event)
|
26
|
+
if respond_to?(method)
|
27
|
+
self.send(method)
|
28
|
+
else
|
29
|
+
raise EventRoutingError.new(event, self, method)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
def response_body
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
include Metal
|
38
|
+
include AbstractController::Callbacks
|
39
|
+
|
21
40
|
# Tell Rails that BaseController and children can be reloaded when in
|
22
41
|
# the Development environment.
|
23
42
|
def self.inherited(controller)
|
@@ -26,33 +45,6 @@ module WebsocketRails
|
|
26
45
|
end
|
27
46
|
end
|
28
47
|
|
29
|
-
# Add observers to specific events or the controller in general. This functionality is similar
|
30
|
-
# to the Rails before_filter methods. Observers are stored as Proc objects and have access
|
31
|
-
# to the current controller environment.
|
32
|
-
#
|
33
|
-
# Observing all events sent to a controller:
|
34
|
-
# class ChatController < WebsocketRails::BaseController
|
35
|
-
# observe {
|
36
|
-
# if data_store.each_user.count > 0
|
37
|
-
# puts 'a user has joined'
|
38
|
-
# end
|
39
|
-
# }
|
40
|
-
# end
|
41
|
-
# Observing a single event that occurrs:
|
42
|
-
# observe(:new_message) {
|
43
|
-
# puts 'new_message has fired!'
|
44
|
-
# }
|
45
|
-
def self.observe(event = nil, &block)
|
46
|
-
# Stores the observer Procs for the current controller. See {observe} for details.
|
47
|
-
@observers ||= Hash.new {|h,k| h[k] = Array.new}
|
48
|
-
|
49
|
-
if event
|
50
|
-
@observers[event] << block
|
51
|
-
else
|
52
|
-
@observers[:general] << block
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
48
|
# Provides direct access to the connection object for the client that
|
57
49
|
# initiated the event that is currently being executed.
|
58
50
|
def connection
|
@@ -136,7 +128,11 @@ module WebsocketRails
|
|
136
128
|
end
|
137
129
|
|
138
130
|
def request
|
139
|
-
|
131
|
+
connection.request
|
132
|
+
end
|
133
|
+
|
134
|
+
def action_name
|
135
|
+
@_action_name
|
140
136
|
end
|
141
137
|
|
142
138
|
# Provides access to the {DataStore} for the current controller. The {DataStore} provides convenience
|
@@ -150,24 +146,16 @@ module WebsocketRails
|
|
150
146
|
connection.data_store
|
151
147
|
end
|
152
148
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
# first and event specific observers are executed last. Each will be executed in the order that
|
157
|
-
# they have been defined. This method is executed by the {Dispatcher}.
|
158
|
-
def execute_observers(event)
|
159
|
-
observers = self.class.instance_variable_get(:@observers)
|
160
|
-
|
161
|
-
return unless observers
|
149
|
+
def self.controller_name
|
150
|
+
self.name.underscore.gsub(/_controller$/,'')
|
151
|
+
end
|
162
152
|
|
163
|
-
|
164
|
-
|
165
|
-
end
|
166
|
-
observers[event].each do |observer|
|
167
|
-
instance_eval( &observer )
|
168
|
-
end
|
153
|
+
def controller_name
|
154
|
+
self.class.controller_name
|
169
155
|
end
|
170
156
|
|
157
|
+
private
|
158
|
+
|
171
159
|
def delegate
|
172
160
|
connection.controller_delegate
|
173
161
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/hash_with_indifferent_access'
|
2
|
-
|
3
1
|
module WebsocketRails
|
4
2
|
|
5
3
|
class << self
|
@@ -19,7 +17,7 @@ module WebsocketRails
|
|
19
17
|
attr_reader :channels
|
20
18
|
|
21
19
|
def initialize
|
22
|
-
@channels =
|
20
|
+
@channels = {}.with_indifferent_access
|
23
21
|
end
|
24
22
|
|
25
23
|
def [](channel)
|
@@ -1,6 +1,14 @@
|
|
1
1
|
module WebsocketRails
|
2
2
|
class Configuration
|
3
3
|
|
4
|
+
def user_identifier
|
5
|
+
@user_identifier ||= :id
|
6
|
+
end
|
7
|
+
|
8
|
+
def user_identifier=(identifier)
|
9
|
+
@user_identifier = identifier
|
10
|
+
end
|
11
|
+
|
4
12
|
def keep_subscribers_when_private?
|
5
13
|
@keep_subscribers_when_private ||= false
|
6
14
|
end
|
@@ -9,7 +9,7 @@ module WebsocketRails
|
|
9
9
|
@adapters.unshift adapter
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.establish_connection(request,dispatcher)
|
12
|
+
def self.establish_connection(request, dispatcher)
|
13
13
|
adapter = adapters.detect { |a| a.accepts?( request.env ) } || (raise InvalidConnectionError)
|
14
14
|
adapter.new request, dispatcher
|
15
15
|
end
|
@@ -36,8 +36,10 @@ module WebsocketRails
|
|
36
36
|
@queue = EventQueue.new
|
37
37
|
@data_store = DataStore::Connection.new(self)
|
38
38
|
@delegate = WebsocketRails::DelegationController.new
|
39
|
-
@delegate.instance_variable_set(:@_env,request.env)
|
40
|
-
@delegate.instance_variable_set(:@_request,request)
|
39
|
+
@delegate.instance_variable_set(:@_env, request.env)
|
40
|
+
@delegate.instance_variable_set(:@_request, request)
|
41
|
+
|
42
|
+
WebsocketRails.users[user_identifier] = self
|
41
43
|
start_ping_timer
|
42
44
|
end
|
43
45
|
|
@@ -92,6 +94,14 @@ module WebsocketRails
|
|
92
94
|
send message
|
93
95
|
end
|
94
96
|
|
97
|
+
def send_message(event_name, data = {}, options = {})
|
98
|
+
options.merge! :user_id => user_identifier, :connection => self
|
99
|
+
options[:data] = data
|
100
|
+
|
101
|
+
event = Event.new(event_name, options)
|
102
|
+
event.trigger
|
103
|
+
end
|
104
|
+
|
95
105
|
def send(message)
|
96
106
|
raise NotImplementedError, "Override this method in the connection specific adapter class"
|
97
107
|
end
|
@@ -118,14 +128,29 @@ module WebsocketRails
|
|
118
128
|
|
119
129
|
private
|
120
130
|
|
131
|
+
def user_identifier
|
132
|
+
@user_identifier ||= begin
|
133
|
+
identifier = WebsocketRails.config.user_identifier
|
134
|
+
|
135
|
+
unless @delegate.respond_to?(:current_user) &&
|
136
|
+
@delegate.current_user &&
|
137
|
+
@delegate.current_user.respond_to?(identifier)
|
138
|
+
return id
|
139
|
+
end
|
140
|
+
|
141
|
+
controller_delegate.current_user.send(identifier)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
121
145
|
def dispatch(event)
|
122
|
-
dispatcher.dispatch
|
146
|
+
dispatcher.dispatch event
|
123
147
|
end
|
124
148
|
|
125
149
|
def close_connection
|
126
150
|
@data_store.destroy!
|
127
151
|
@ping_timer.try(:cancel)
|
128
152
|
dispatcher.connection_manager.close_connection self
|
153
|
+
WebsocketRails.users.delete(user_identifier)
|
129
154
|
end
|
130
155
|
|
131
156
|
attr_accessor :pong
|
@@ -11,11 +11,11 @@ module WebsocketRails
|
|
11
11
|
|
12
12
|
# TODO: Add deprecation notice for user defined
|
13
13
|
# instance variables.
|
14
|
-
def new_for_event(event, controller_class)
|
14
|
+
def new_for_event(event, controller_class, method)
|
15
15
|
controller_class = reload!(controller_class)
|
16
16
|
controller = controller_class.new
|
17
17
|
|
18
|
-
prepare(controller, event)
|
18
|
+
prepare(controller, event, method)
|
19
19
|
|
20
20
|
controller
|
21
21
|
end
|
@@ -26,10 +26,11 @@ module WebsocketRails
|
|
26
26
|
@controller_stores[controller.class] ||= DataStore::Controller.new(controller)
|
27
27
|
end
|
28
28
|
|
29
|
-
def prepare(controller, event)
|
29
|
+
def prepare(controller, event, method)
|
30
30
|
set_event(controller, event)
|
31
31
|
set_dispatcher(controller, dispatcher)
|
32
32
|
set_controller_store(controller)
|
33
|
+
set_action_name(controller, method)
|
33
34
|
initialize_controller(controller)
|
34
35
|
end
|
35
36
|
|
@@ -45,6 +46,10 @@ module WebsocketRails
|
|
45
46
|
set_ivar :@_controller_store, controller, store_for_controller(controller)
|
46
47
|
end
|
47
48
|
|
49
|
+
def set_action_name(controller, method)
|
50
|
+
set_ivar :@_action_name, controller, method
|
51
|
+
end
|
52
|
+
|
48
53
|
def set_ivar(ivar, object, value)
|
49
54
|
object.instance_variable_set(ivar, value)
|
50
55
|
end
|
@@ -67,7 +72,7 @@ module WebsocketRails
|
|
67
72
|
load "#{filename}.rb"
|
68
73
|
return class_name.constantize
|
69
74
|
end
|
70
|
-
|
75
|
+
|
71
76
|
return controller
|
72
77
|
end
|
73
78
|
|
@@ -102,8 +102,8 @@ module WebsocketRails
|
|
102
102
|
#
|
103
103
|
#
|
104
104
|
# class AccountController < WebsocketRails::BaseController
|
105
|
-
# # We will use
|
106
|
-
#
|
105
|
+
# # We will use a before filter to set the initial value
|
106
|
+
# before_action { controller_store[:event_count] ||= 0 }
|
107
107
|
#
|
108
108
|
# # Mapped as `accounts.important_event` in the Event Router
|
109
109
|
# def important_event
|
@@ -114,11 +114,11 @@ module WebsocketRails
|
|
114
114
|
# end
|
115
115
|
#
|
116
116
|
# class ProductController < WebsocketRails::BaseController
|
117
|
-
# # We will use
|
118
|
-
#
|
117
|
+
# # We will use a before filter to set the initial value
|
118
|
+
# before_action { controller_store[:event_count] ||= 0 }
|
119
119
|
#
|
120
120
|
# # Mapped as `products.boring_event` in the Event Router
|
121
|
-
# def boring_event
|
121
|
+
# def boring_event
|
122
122
|
# # This will be private for each controller
|
123
123
|
# controller_store[:event_count] += 1
|
124
124
|
# trigger_success controller_store[:event_count]
|
@@ -27,6 +27,7 @@ module WebsocketRails
|
|
27
27
|
if event.is_channel?
|
28
28
|
WebsocketRails[event.channel].trigger_event event
|
29
29
|
else
|
30
|
+
reload_event_map! unless event.is_internal?
|
30
31
|
route event
|
31
32
|
end
|
32
33
|
end
|
@@ -41,6 +42,16 @@ module WebsocketRails
|
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
45
|
+
def reload_event_map!
|
46
|
+
return unless defined?(Rails) and !Rails.configuration.cache_classes
|
47
|
+
begin
|
48
|
+
load "#{Rails.root}/config/events.rb"
|
49
|
+
@event_map = EventMap.new(self)
|
50
|
+
rescue Exception => ex
|
51
|
+
log(:warn, "EventMap reload failed: #{ex.message}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
44
55
|
private
|
45
56
|
|
46
57
|
def route(event)
|
@@ -49,15 +60,9 @@ module WebsocketRails
|
|
49
60
|
actions << Fiber.new do
|
50
61
|
begin
|
51
62
|
log_event(event) do
|
52
|
-
controller = controller_factory.new_for_event(event, controller_class)
|
53
|
-
|
54
|
-
controller.send(:execute_observers, event.name)
|
63
|
+
controller = controller_factory.new_for_event(event, controller_class, method)
|
55
64
|
|
56
|
-
|
57
|
-
controller.send(method)
|
58
|
-
else
|
59
|
-
raise EventRoutingError.new(event, controller, method)
|
60
|
-
end
|
65
|
+
controller.process_action(method, event)
|
61
66
|
end
|
62
67
|
rescue Exception => ex
|
63
68
|
event.success = false
|
@@ -93,11 +93,11 @@ module WebsocketRails
|
|
93
93
|
include Logging
|
94
94
|
extend StaticEvents
|
95
95
|
|
96
|
-
attr_reader :id, :name, :connection, :namespace, :channel
|
96
|
+
attr_reader :id, :name, :connection, :namespace, :channel, :user_id
|
97
97
|
|
98
98
|
attr_accessor :data, :result, :success, :server_token
|
99
99
|
|
100
|
-
def initialize(event_name,options={})
|
100
|
+
def initialize(event_name, options={})
|
101
101
|
case event_name
|
102
102
|
when String
|
103
103
|
namespace = event_name.split('.')
|
@@ -111,6 +111,7 @@ module WebsocketRails
|
|
111
111
|
@channel = options[:channel].to_sym if options[:channel]
|
112
112
|
@connection = options[:connection]
|
113
113
|
@server_token = options[:server_token]
|
114
|
+
@user_id = options[:user_id]
|
114
115
|
@namespace = validate_namespace( options[:namespace] || namespace )
|
115
116
|
end
|
116
117
|
|
@@ -120,6 +121,7 @@ module WebsocketRails
|
|
120
121
|
{
|
121
122
|
:id => id,
|
122
123
|
:channel => channel,
|
124
|
+
:user_id => user_id,
|
123
125
|
:data => data,
|
124
126
|
:success => success,
|
125
127
|
:result => result,
|
@@ -132,6 +134,10 @@ module WebsocketRails
|
|
132
134
|
!@channel.nil?
|
133
135
|
end
|
134
136
|
|
137
|
+
def is_user?
|
138
|
+
!@user_id.nil? && !is_channel?
|
139
|
+
end
|
140
|
+
|
135
141
|
def is_invalid?
|
136
142
|
name == :invalid_event
|
137
143
|
end
|
@@ -163,7 +169,5 @@ module WebsocketRails
|
|
163
169
|
namespace.map(&:to_sym) rescue [:global]
|
164
170
|
end
|
165
171
|
|
166
|
-
|
167
|
-
|
168
172
|
end
|
169
173
|
end
|
@@ -55,12 +55,18 @@ module WebsocketRails
|
|
55
55
|
fiber_redis = Redis.connect(WebsocketRails.config.redis_options)
|
56
56
|
fiber_redis.subscribe "websocket_rails.events" do |on|
|
57
57
|
|
58
|
-
on.message do |
|
58
|
+
on.message do |_, encoded_event|
|
59
59
|
event = Event.new_from_json(encoded_event, nil)
|
60
|
+
|
61
|
+
# Do nothing if this is the server that sent this event.
|
60
62
|
next if event.server_token == server_token
|
61
63
|
|
64
|
+
# Ensure an event never gets triggered twice. Events added to the
|
65
|
+
# redis queue from other processes may not have a server token
|
66
|
+
# attached.
|
62
67
|
event.server_token = server_token if event.server_token.nil?
|
63
|
-
|
68
|
+
|
69
|
+
trigger_incoming event
|
64
70
|
end
|
65
71
|
end
|
66
72
|
|
@@ -72,17 +78,28 @@ module WebsocketRails
|
|
72
78
|
EM.next_tick { synchro.resume }
|
73
79
|
|
74
80
|
trap('TERM') do
|
75
|
-
shutdown!
|
81
|
+
Thread.new { shutdown! }
|
76
82
|
end
|
77
83
|
trap('INT') do
|
78
|
-
shutdown!
|
84
|
+
Thread.new { shutdown! }
|
79
85
|
end
|
80
86
|
trap('QUIT') do
|
81
|
-
shutdown!
|
87
|
+
Thread.new { shutdown! }
|
82
88
|
end
|
83
89
|
end
|
84
90
|
end
|
85
91
|
|
92
|
+
def trigger_incoming(event)
|
93
|
+
case
|
94
|
+
when event.is_channel?
|
95
|
+
WebsocketRails[event.channel].trigger_event(event)
|
96
|
+
when event.is_user?
|
97
|
+
connection = WebsocketRails.users[event.user_id]
|
98
|
+
return if connection.nil?
|
99
|
+
connection.trigger event
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
86
103
|
def shutdown!
|
87
104
|
remove_server(server_token)
|
88
105
|
end
|
@@ -103,11 +120,9 @@ module WebsocketRails
|
|
103
120
|
end
|
104
121
|
|
105
122
|
def remove_server(token)
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
EM.stop
|
110
|
-
end.resume
|
123
|
+
ruby_redis.srem "websocket_rails.active_servers", token
|
124
|
+
info "Server Removed: #{token}"
|
125
|
+
EM.stop
|
111
126
|
end
|
112
127
|
|
113
128
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module WebsocketRails
|
2
|
+
|
3
|
+
# Contains a Hash of all connected users. This
|
4
|
+
# can be used to trigger an event on a specific
|
5
|
+
# user from outside of a WebsocketRails controller.
|
6
|
+
#
|
7
|
+
# The key for a particular user is defined in the
|
8
|
+
# configuration as `config.user_identifier`.
|
9
|
+
#
|
10
|
+
# If there is a `current_user` method defined
|
11
|
+
# in ApplicationController and a user is signed
|
12
|
+
# in to your application when the connection is
|
13
|
+
# opened, WebsocketRails will call the method
|
14
|
+
# defined in `config.user_identifier` on the
|
15
|
+
# `current_user` object and use that value as
|
16
|
+
# the key.
|
17
|
+
#
|
18
|
+
# # In your events.rb file
|
19
|
+
# WebsocketRails.setup do |config|
|
20
|
+
# # Defaults to :id
|
21
|
+
# config.user_identifier = :name
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # In a standard controller or background job
|
25
|
+
# name = current_user.name
|
26
|
+
# WebsocketRails.users[name].send_message :event_name, data
|
27
|
+
#
|
28
|
+
# If no `current_user` method is defined or the
|
29
|
+
# user is not signed in when the WebsocketRails
|
30
|
+
# connection is opened, the key will default to
|
31
|
+
# `connection.id`.
|
32
|
+
def self.users
|
33
|
+
@user_manager ||= UserManager.new
|
34
|
+
end
|
35
|
+
|
36
|
+
class UserManager
|
37
|
+
|
38
|
+
attr_reader :users
|
39
|
+
|
40
|
+
def initialize
|
41
|
+
@users = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](identifier)
|
45
|
+
unless user = @users[identifier]
|
46
|
+
user = MissingUser.new(identifier)
|
47
|
+
end
|
48
|
+
user
|
49
|
+
end
|
50
|
+
|
51
|
+
def []=(identifier, connection)
|
52
|
+
@users[identifier] = connection
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete(identifier)
|
56
|
+
@users.delete(identifier)
|
57
|
+
end
|
58
|
+
|
59
|
+
class MissingUser
|
60
|
+
|
61
|
+
def initialize(identifier)
|
62
|
+
@identifier = identifier
|
63
|
+
end
|
64
|
+
|
65
|
+
def send_message(event_name, data = {}, options = {})
|
66
|
+
if WebsocketRails.synchronize?
|
67
|
+
options.merge! :user_id => @identifier
|
68
|
+
options[:data] = data
|
69
|
+
|
70
|
+
event = Event.new(event_name, options)
|
71
|
+
Synchronization.publish event
|
72
|
+
true
|
73
|
+
else
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def nil?
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -1,53 +1,53 @@
|
|
1
1
|
class ChatController < WebsocketRails::BaseController
|
2
2
|
|
3
|
-
|
3
|
+
before_action do
|
4
4
|
if message_counter > 10
|
5
5
|
self.message_counter = 0
|
6
6
|
end
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
end
|
8
|
+
|
9
|
+
before_action :only => :new_message do
|
10
10
|
true
|
11
|
-
|
12
|
-
|
11
|
+
end
|
12
|
+
|
13
13
|
attr_accessor :message_counter
|
14
14
|
|
15
15
|
def initialize
|
16
16
|
# perform application setup here
|
17
17
|
@message_counter = 0
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def client_connected
|
21
21
|
# do something when a client connects
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def error_occurred
|
25
25
|
# do something when an error occurs
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def new_message
|
29
29
|
@message_counter += 1
|
30
30
|
broadcast_message :new_message, message
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def new_user
|
34
34
|
controller_store[:user] = message
|
35
35
|
broadcast_user_list
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def change_username
|
39
39
|
controller_store[:user] = message
|
40
40
|
broadcast_user_list
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
def delete_user
|
44
44
|
controller_store[:user] = nil
|
45
45
|
broadcast_user_list
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def broadcast_user_list
|
49
49
|
users = ['user']
|
50
50
|
broadcast_message :user_list, users
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
end
|
@@ -15,8 +15,12 @@ WebSocket Interface for the WebSocketRails client.
|
|
15
15
|
this.on_close = __bind(this.on_close, this);
|
16
16
|
this.on_message = __bind(this.on_message, this);
|
17
17
|
this.trigger = __bind(this.trigger, this);
|
18
|
-
if (
|
18
|
+
if (this.url.match(/^wss?:\/\//)) {
|
19
|
+
console.log("WARNING: Using connection urls with protocol specified is depricated");
|
20
|
+
} else if (window.location.protocol === 'http:') {
|
19
21
|
this.url = "ws://" + this.url;
|
22
|
+
} else {
|
23
|
+
this.url = "wss://" + this.url;
|
20
24
|
}
|
21
25
|
this.message_queue = [];
|
22
26
|
this._conn = new WebSocket(this.url);
|
@@ -43,6 +43,18 @@ module WebsocketRails
|
|
43
43
|
it "should create a new DataStore::Connection instance" do
|
44
44
|
subject.data_store.should be_a DataStore::Connection
|
45
45
|
end
|
46
|
+
|
47
|
+
before do
|
48
|
+
WebsocketRails.config.stub(:user_identifier).and_return(:name)
|
49
|
+
WebsocketRails::DelegationController.any_instance
|
50
|
+
.stub_chain(:current_user, :name)
|
51
|
+
.and_return('Frank')
|
52
|
+
subject
|
53
|
+
end
|
54
|
+
|
55
|
+
it "adds itself to the UserManager Hash" do
|
56
|
+
WebsocketRails.users['Frank'].should == subject
|
57
|
+
end
|
46
58
|
end
|
47
59
|
|
48
60
|
describe "#on_open" do
|
@@ -69,6 +81,11 @@ module WebsocketRails
|
|
69
81
|
dispatcher.should_receive(:dispatch).with(on_close_event)
|
70
82
|
subject.on_close("data")
|
71
83
|
end
|
84
|
+
|
85
|
+
it "removes itself from the global UserMnaager" do
|
86
|
+
subject.on_close
|
87
|
+
WebsocketRails.users[subject.id].nil?.should == true
|
88
|
+
end
|
72
89
|
end
|
73
90
|
|
74
91
|
describe "#on_error" do
|
@@ -87,9 +104,36 @@ module WebsocketRails
|
|
87
104
|
end
|
88
105
|
end
|
89
106
|
|
107
|
+
describe "#send_message" do
|
108
|
+
before do
|
109
|
+
Event.any_instance.stub(:trigger)
|
110
|
+
end
|
111
|
+
after do
|
112
|
+
subject.send_message :message, "some_data"
|
113
|
+
end
|
114
|
+
|
115
|
+
it "creates and triggers a new event" do
|
116
|
+
Event.any_instance.should_receive(:trigger)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "sets it's user identifier on the event" do
|
120
|
+
subject.stub(:user_identifier).and_return(:some_name_or_id)
|
121
|
+
Event.should_receive(:new) do |name, options|
|
122
|
+
options[:user_id].should == :some_name_or_id
|
123
|
+
end.and_call_original
|
124
|
+
end
|
125
|
+
|
126
|
+
it "sets the connection property of the event correctly" do
|
127
|
+
subject.stub(:user_identifier).and_return(:some_name_or_id)
|
128
|
+
Event.should_receive(:new) do |name, options|
|
129
|
+
options[:connection].should == subject
|
130
|
+
end.and_call_original
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
90
134
|
describe "#send" do
|
91
135
|
it "should raise a NotImplementedError exception" do
|
92
|
-
expect { subject.send :message }.to raise_exception(
|
136
|
+
expect { subject.send :message }.to raise_exception(NotImplementedError)
|
93
137
|
end
|
94
138
|
end
|
95
139
|
|
@@ -38,37 +38,37 @@ module WebsocketRails
|
|
38
38
|
rails_env = double(:rails_env)
|
39
39
|
Rails.stub(:env).and_return rails_env
|
40
40
|
rails_env.stub(:development?).and_return true
|
41
|
-
controller = subject.new_for_event(event, InternalController)
|
41
|
+
controller = subject.new_for_event(event, InternalController, 'some_method')
|
42
42
|
controller.class.should == InternalController
|
43
43
|
end
|
44
44
|
|
45
45
|
end
|
46
46
|
|
47
47
|
it "creates and returns a new controller instance" do
|
48
|
-
controller = subject.new_for_event(event, TestController)
|
48
|
+
controller = subject.new_for_event(event, TestController, 'some_method')
|
49
49
|
controller.class.should == TestController
|
50
50
|
end
|
51
51
|
|
52
52
|
it "initializes the controller with the correct data_store" do
|
53
53
|
store = double('data_store')
|
54
54
|
subject.controller_stores[TestController] = store
|
55
|
-
controller = subject.new_for_event(event, TestController)
|
55
|
+
controller = subject.new_for_event(event, TestController, 'some_method')
|
56
56
|
controller.controller_store.should == store
|
57
57
|
end
|
58
58
|
|
59
59
|
it "initializes the controller with the correct event" do
|
60
|
-
controller = subject.new_for_event(event, TestController)
|
60
|
+
controller = subject.new_for_event(event, TestController, 'some_method')
|
61
61
|
controller.event.should == event
|
62
62
|
end
|
63
63
|
|
64
64
|
it "initializes the controller with the correct dispatcher" do
|
65
|
-
controller = subject.new_for_event(event, TestController)
|
65
|
+
controller = subject.new_for_event(event, TestController, 'some_method')
|
66
66
|
controller._dispatcher.should == dispatcher
|
67
67
|
end
|
68
68
|
|
69
69
|
it "calls #initialize_session on the controller only once" do
|
70
70
|
TestController.any_instance.should_receive(:initialize_session).once
|
71
|
-
3.times { subject.new_for_event(event, TestController) }
|
71
|
+
3.times { subject.new_for_event(event, TestController, 'some_method') }
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
@@ -20,21 +20,12 @@ def set_temp_module_const(mod, name, value, &block)
|
|
20
20
|
mod.send(:remove_const, name)
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
23
|
module WebsocketRails
|
25
24
|
|
26
|
-
|
27
|
-
|
28
25
|
class EventTarget
|
29
26
|
attr_reader :_event, :_dispatcher, :test_method
|
30
|
-
|
31
|
-
def execute_observers(event_name)
|
32
|
-
true
|
33
|
-
end
|
34
27
|
end
|
35
28
|
|
36
|
-
|
37
|
-
|
38
29
|
describe Dispatcher do
|
39
30
|
|
40
31
|
let(:event) { double('Event').as_null_object }
|
@@ -59,6 +50,7 @@ module WebsocketRails
|
|
59
50
|
|
60
51
|
describe "#receive" do
|
61
52
|
before { Event.stub(:new).and_return( event ) }
|
53
|
+
|
62
54
|
it "should dispatch a new event" do
|
63
55
|
subject.stub(:dispatch) do |dispatch_event|
|
64
56
|
dispatch_event.should == event
|
@@ -79,7 +71,7 @@ module WebsocketRails
|
|
79
71
|
end
|
80
72
|
|
81
73
|
it "should execute the correct method on the target class" do
|
82
|
-
EventTarget.any_instance.should_receive(:test_method)
|
74
|
+
EventTarget.any_instance.should_receive(:process_action).with(:test_method, event)
|
83
75
|
subject.dispatch(event)
|
84
76
|
end
|
85
77
|
|
@@ -167,7 +159,6 @@ module WebsocketRails
|
|
167
159
|
|
168
160
|
end
|
169
161
|
|
170
|
-
|
171
162
|
context 'when ActiveRecord::RecordInvalid is not defined' do
|
172
163
|
|
173
164
|
it 'should check that exception can be converted to JSON' do
|
@@ -181,6 +172,5 @@ module WebsocketRails
|
|
181
172
|
end
|
182
173
|
|
183
174
|
end
|
184
|
-
|
185
175
|
end
|
186
176
|
end
|
data/spec/unit/event_spec.rb
CHANGED
@@ -6,7 +6,7 @@ module WebsocketRails
|
|
6
6
|
let(:encoded_message_string) { '["new_message",{"id":"1234","data":"this is a message"}]' }
|
7
7
|
let(:namespace_encoded_message_string) { '["product.new_message",{"id":"1234","data":"this is a message"}]' }
|
8
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","data":"this is a message","success":null,"result":null,"server_token":"1234"}]' }
|
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,"server_token":"1234"}]' }
|
10
10
|
let(:synchronizable_encoded_message) { '["new_message",{"id":"1234","data":{"message":"this is a message"},"server_token":"1234"}]' }
|
11
11
|
let(:connection) { double('connection') }
|
12
12
|
|
@@ -96,6 +96,13 @@ module WebsocketRails
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
describe "#is_user?" do
|
100
|
+
it "returns true if the event is meant for a specific user" do
|
101
|
+
event = Event.new "event", :data => "data", :user_id => :username
|
102
|
+
event.is_user?
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
99
106
|
describe "#is_invalid?" do
|
100
107
|
it "returns true if the event name is :invalid_event" do
|
101
108
|
event = Event.new(:invalid_event)
|
@@ -2,12 +2,23 @@ require "spec_helper"
|
|
2
2
|
require "eventmachine"
|
3
3
|
|
4
4
|
module WebsocketRails
|
5
|
+
#class Synchronization
|
6
|
+
# def test_block(channel, &block)
|
7
|
+
# # do nothing beyatch
|
8
|
+
# block.call
|
9
|
+
# end
|
10
|
+
|
11
|
+
# def synchronize!
|
12
|
+
# test_block("something") { raise "FTW!" }
|
13
|
+
# end
|
14
|
+
#end
|
15
|
+
|
5
16
|
describe Synchronization do
|
6
17
|
|
7
18
|
around(:each) do |example|
|
8
19
|
EM.run do
|
9
20
|
Fiber.new do
|
10
|
-
@redis = Redis.new
|
21
|
+
@redis = Redis.new(WebsocketRails.config.redis_options)
|
11
22
|
@redis.del "websocket_rails.active_servers"
|
12
23
|
example.run
|
13
24
|
end.resume
|
@@ -15,6 +26,7 @@ module WebsocketRails
|
|
15
26
|
end
|
16
27
|
|
17
28
|
after(:each) do
|
29
|
+
@redis.del "websocket_rails.active_servers"
|
18
30
|
EM.stop
|
19
31
|
end
|
20
32
|
|
@@ -30,36 +42,43 @@ module WebsocketRails
|
|
30
42
|
end
|
31
43
|
|
32
44
|
describe "#synchronize!" do
|
33
|
-
#
|
34
|
-
|
35
|
-
#end
|
36
|
-
|
37
|
-
#it "should receive remote channel events" do
|
38
|
-
# event = Event.new(:channel_event, :channel => :channel_one, :data => 'hello channel one')
|
45
|
+
# need to add an integration test to cover this.
|
46
|
+
end
|
39
47
|
|
40
|
-
|
41
|
-
|
48
|
+
describe "#trigger_incoming" do
|
49
|
+
context "when dispatching channel events" do
|
50
|
+
before do
|
51
|
+
@event = Event.new(:channel_event, :channel => :channel_one, :data => 'hello channel one')
|
52
|
+
end
|
42
53
|
|
43
|
-
|
54
|
+
it "triggers the event on the correct channel" do
|
55
|
+
WebsocketRails[:channel_one].should_receive(:trigger_event).with @event
|
56
|
+
subject.trigger_incoming @event
|
57
|
+
end
|
58
|
+
end
|
44
59
|
|
45
|
-
|
60
|
+
context "when dispatching user events" do
|
61
|
+
before do
|
62
|
+
@event = Event.new(:channel_event, :user_id => :username, :data => 'hello channel one')
|
63
|
+
end
|
46
64
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
event = Event.new(:redis_event, :channel => 'synchrony', :data => 'hello from another process')
|
52
|
-
if event.server_token.nil?
|
53
|
-
event.server_token = subject.server_token
|
65
|
+
context "and the user is not connected to this server" do
|
66
|
+
it "does nothing" do
|
67
|
+
subject.trigger_incoming(@event).should == nil
|
68
|
+
end
|
54
69
|
end
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
70
|
+
|
71
|
+
context "and the user is connected to this server" do
|
72
|
+
before do
|
73
|
+
@connection = double('Connection')
|
74
|
+
WebsocketRails.users[:username] = @connection
|
75
|
+
end
|
76
|
+
|
77
|
+
it "triggers the event on the correct user" do
|
78
|
+
WebsocketRails.users[:username].should_receive(:trigger).with @event
|
79
|
+
subject.trigger_incoming @event
|
80
|
+
end
|
61
81
|
end
|
62
|
-
event.server_token.should == '1234'
|
63
82
|
end
|
64
83
|
end
|
65
84
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module WebsocketRails
|
4
|
+
|
5
|
+
describe ".users" do
|
6
|
+
it "returns the global instance of UserManager" do
|
7
|
+
WebsocketRails.users.should be_a UserManager
|
8
|
+
end
|
9
|
+
|
10
|
+
context "when synchronization is enabled" do
|
11
|
+
before do
|
12
|
+
WebsocketRails.stub(:synchronize?).and_return(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "and the user is not connected to this worker" do
|
16
|
+
it "publishes the event to redis" do
|
17
|
+
Synchronization.should_receive(:publish) do |event|
|
18
|
+
event.user_id.should == :missing
|
19
|
+
end
|
20
|
+
|
21
|
+
WebsocketRails.users[:missing].send_message :test, :data
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe UserManager do
|
28
|
+
|
29
|
+
let(:connection) { double('Connection') }
|
30
|
+
|
31
|
+
describe "#[]=" do
|
32
|
+
it "store's a reference to a connection in the user's hash" do
|
33
|
+
subject[:username] = connection
|
34
|
+
subject.users[:username].should == connection
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#[]" do
|
39
|
+
before do
|
40
|
+
subject[:username] = connection
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when passed a known user identifier" do
|
44
|
+
it "returns that user's connection" do
|
45
|
+
subject[:username].should == connection
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#delete" do
|
51
|
+
before do
|
52
|
+
subject[:username] = connection
|
53
|
+
end
|
54
|
+
|
55
|
+
it "deletes the connection from the users hash" do
|
56
|
+
subject.delete(:username)
|
57
|
+
subject[:username].should be_a UserManager::MissingUser
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: websocket-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2013-
|
14
|
+
date: 2013-09-03 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rails
|
@@ -190,6 +190,7 @@ files:
|
|
190
190
|
- lib/config.ru
|
191
191
|
- lib/generators/websocket_rails/install/install_generator.rb
|
192
192
|
- lib/generators/websocket_rails/install/templates/events.rb
|
193
|
+
- lib/generators/websocket_rails/install/templates/websocket_rails.rb
|
193
194
|
- lib/rails/app/controllers/websocket_rails/delegation_controller.rb
|
194
195
|
- lib/rails/config/routes.rb
|
195
196
|
- lib/rails/tasks/websocket_rails.tasks
|
@@ -216,6 +217,7 @@ files:
|
|
216
217
|
- lib/websocket_rails/logging.rb
|
217
218
|
- lib/websocket_rails/spec_helpers.rb
|
218
219
|
- lib/websocket_rails/synchronization.rb
|
220
|
+
- lib/websocket_rails/user_manager.rb
|
219
221
|
- lib/websocket_rails/version.rb
|
220
222
|
- bin/thin-socketrails
|
221
223
|
- spec/dummy/app/controllers/application_controller.rb
|
@@ -294,6 +296,7 @@ files:
|
|
294
296
|
- spec/unit/logging_spec.rb
|
295
297
|
- spec/unit/synchronization_spec.rb
|
296
298
|
- spec/unit/target_validator_spec.rb
|
299
|
+
- spec/unit/user_manager_spec.rb
|
297
300
|
- MIT-LICENSE
|
298
301
|
- Rakefile
|
299
302
|
- Gemfile
|
@@ -301,7 +304,7 @@ files:
|
|
301
304
|
- CHANGELOG.md
|
302
305
|
homepage: http://danknox.github.com/websocket-rails/
|
303
306
|
licenses: []
|
304
|
-
post_install_message: Welcome to WebsocketRails v0.
|
307
|
+
post_install_message: Welcome to WebsocketRails v0.5.0!
|
305
308
|
rdoc_options: []
|
306
309
|
require_paths:
|
307
310
|
- lib
|
@@ -313,7 +316,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
313
316
|
version: '0'
|
314
317
|
segments:
|
315
318
|
- 0
|
316
|
-
hash: -
|
319
|
+
hash: -3442295277156714841
|
317
320
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
318
321
|
none: false
|
319
322
|
requirements:
|
@@ -322,7 +325,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
322
325
|
version: '0'
|
323
326
|
segments:
|
324
327
|
- 0
|
325
|
-
hash: -
|
328
|
+
hash: -3442295277156714841
|
326
329
|
requirements: []
|
327
330
|
rubyforge_project: websocket-rails
|
328
331
|
rubygems_version: 1.8.25
|