websocket-rails 0.4.9 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|