websocket-rails 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +11 -2
- data/lib/generators/websocket_rails/install/templates/websocket_rails.rb +7 -0
- data/lib/websocket_rails/configuration.rb +8 -0
- data/lib/websocket_rails/connection_adapters.rb +34 -29
- data/lib/websocket_rails/connection_manager.rb +35 -9
- data/lib/websocket_rails/event.rb +6 -2
- data/lib/websocket_rails/synchronization.rb +48 -3
- data/lib/websocket_rails/user_manager.rb +210 -19
- data/lib/websocket_rails/version.rb +1 -1
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/config/environments/test.rb +1 -2
- data/spec/dummy/config/{initializers/events.rb → events.rb} +0 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20130902222552_create_users.rb +10 -0
- data/spec/dummy/db/schema.rb +23 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +17 -0
- data/spec/integration/connection_manager_spec.rb +2 -2
- data/spec/unit/connection_adapters_spec.rb +77 -41
- data/spec/unit/connection_manager_spec.rb +45 -9
- data/spec/unit/synchronization_spec.rb +47 -18
- data/spec/unit/user_manager_spec.rb +115 -11
- metadata +12 -7
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# WebsocketRails Change Log
|
2
2
|
|
3
|
+
## Version 0.6.0
|
4
|
+
|
5
|
+
September 3 2013
|
6
|
+
|
7
|
+
* Added the UserManager accessible through the `WebsocketRails.users`
|
8
|
+
method. This allows for triggering events on individual logged in users
|
9
|
+
from anywhere inside of your application without the need to create a
|
10
|
+
channel for that user.
|
11
|
+
|
3
12
|
## Version 0.5.0
|
4
13
|
|
5
14
|
September 2 2013
|
@@ -8,13 +17,13 @@ September 2 2013
|
|
8
17
|
shcheme. - Thanks to @depili
|
9
18
|
* Override ConnectionManager#inspect to clean up the output from `rake
|
10
19
|
routes`
|
11
|
-
*
|
20
|
+
* Added a basic Global UserManager for triggering events on specific users
|
12
21
|
from anywhere inside your app without creating a dedicated user channel.
|
13
22
|
* Deprecate the old controller observer system and implement full Rails
|
14
23
|
AbstractController::Callbacks support. - Thanks to @pitr
|
15
24
|
* Reload the events.rb event route file each time an event is fired. -
|
16
25
|
Thanks to @moaa
|
17
|
-
*
|
26
|
+
* Separated the event route file and WebsocketRails configuration files.
|
18
27
|
The events.rb now lives in `config/events.rb`. The configuration should
|
19
28
|
remain in an initializer located at `config/initializers/websocket_rails.rb`. - Thanks to @moaa
|
20
29
|
|
@@ -38,4 +38,11 @@ WebsocketRails.setup do |config|
|
|
38
38
|
# if one exists. If `current_user` does not exist or does not
|
39
39
|
# respond to the identifier, the key will default to `connection.id`
|
40
40
|
# config.user_identifier = :id
|
41
|
+
|
42
|
+
# Uncomment and change this option to override the class associated
|
43
|
+
# with your `current_user` object. This class will be used when
|
44
|
+
# synchronization is enabled and you trigger events from background
|
45
|
+
# jobs using the WebsocketRails.users UserManager.
|
46
|
+
# config.user_class = User
|
47
|
+
|
41
48
|
end
|
@@ -9,6 +9,14 @@ module WebsocketRails
|
|
9
9
|
@user_identifier = identifier
|
10
10
|
end
|
11
11
|
|
12
|
+
def user_class
|
13
|
+
@user_class ||= User
|
14
|
+
end
|
15
|
+
|
16
|
+
def user_class=(klass)
|
17
|
+
@user_class = klass
|
18
|
+
end
|
19
|
+
|
12
20
|
def keep_subscribers_when_private?
|
13
21
|
@keep_subscribers_when_private ||= false
|
14
22
|
end
|
@@ -10,7 +10,7 @@ module WebsocketRails
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.establish_connection(request, dispatcher)
|
13
|
-
adapter = adapters.detect { |a| a.accepts?(
|
13
|
+
adapter = adapters.detect { |a| a.accepts?(request.env) } || raise(InvalidConnectionError)
|
14
14
|
adapter.new request, dispatcher
|
15
15
|
end
|
16
16
|
|
@@ -28,6 +28,10 @@ module WebsocketRails
|
|
28
28
|
|
29
29
|
attr_reader :dispatcher, :queue, :env, :request, :data_store
|
30
30
|
|
31
|
+
# The ConnectionManager will set the connection ID when the
|
32
|
+
# connection is opened.
|
33
|
+
attr_accessor :id
|
34
|
+
|
31
35
|
def initialize(request, dispatcher)
|
32
36
|
@env = request.env.dup
|
33
37
|
@request = request
|
@@ -39,7 +43,6 @@ module WebsocketRails
|
|
39
43
|
@delegate.instance_variable_set(:@_env, request.env)
|
40
44
|
@delegate.instance_variable_set(:@_request, request)
|
41
45
|
|
42
|
-
WebsocketRails.users[user_identifier] = self
|
43
46
|
start_ping_timer
|
44
47
|
end
|
45
48
|
|
@@ -70,28 +73,16 @@ module WebsocketRails
|
|
70
73
|
@queue << event
|
71
74
|
end
|
72
75
|
|
73
|
-
attr_accessor :flush_scheduled
|
74
|
-
|
75
76
|
def trigger(event)
|
76
|
-
# Uncomment when implementing history queueing with redis
|
77
|
-
#enqueue event
|
78
|
-
#unless flush_scheduled
|
79
|
-
# EM.next_tick { flush; flush_scheduled = false }
|
80
|
-
# flush_scheduled = true
|
81
|
-
#end
|
82
77
|
send "[#{event.serialize}]"
|
83
78
|
end
|
84
79
|
|
85
80
|
def flush
|
86
|
-
|
87
|
-
message = "["
|
81
|
+
message = []
|
88
82
|
@queue.flush do |event|
|
89
|
-
message << event.
|
90
|
-
message << "," unless count == @queue.size
|
91
|
-
count += 1
|
83
|
+
message << event.as_json
|
92
84
|
end
|
93
|
-
message
|
94
|
-
send message
|
85
|
+
send message.to_json
|
95
86
|
end
|
96
87
|
|
97
88
|
def send_message(event_name, data = {}, options = {})
|
@@ -110,14 +101,14 @@ module WebsocketRails
|
|
110
101
|
[ -1, {}, [] ]
|
111
102
|
end
|
112
103
|
|
113
|
-
def id
|
114
|
-
object_id.to_i
|
115
|
-
end
|
116
|
-
|
117
104
|
def controller_delegate
|
118
105
|
@delegate
|
119
106
|
end
|
120
107
|
|
108
|
+
def connected?
|
109
|
+
true & @connected
|
110
|
+
end
|
111
|
+
|
121
112
|
def inspect
|
122
113
|
"#<Connection::#{id}>"
|
123
114
|
end
|
@@ -126,31 +117,46 @@ module WebsocketRails
|
|
126
117
|
inspect
|
127
118
|
end
|
128
119
|
|
129
|
-
|
120
|
+
def user_connection?
|
121
|
+
not user_identifier.nil?
|
122
|
+
end
|
123
|
+
|
124
|
+
def user
|
125
|
+
return unless user_connection?
|
126
|
+
controller_delegate.current_user
|
127
|
+
end
|
130
128
|
|
131
129
|
def user_identifier
|
132
130
|
@user_identifier ||= begin
|
133
131
|
identifier = WebsocketRails.config.user_identifier
|
134
132
|
|
135
|
-
unless
|
136
|
-
@delegate.current_user &&
|
137
|
-
@delegate.current_user.respond_to?(identifier)
|
138
|
-
return id
|
139
|
-
end
|
133
|
+
return unless current_user_responds_to?(identifier)
|
140
134
|
|
141
135
|
controller_delegate.current_user.send(identifier)
|
142
136
|
end
|
143
137
|
end
|
144
138
|
|
139
|
+
private
|
140
|
+
|
145
141
|
def dispatch(event)
|
146
142
|
dispatcher.dispatch event
|
147
143
|
end
|
148
144
|
|
145
|
+
def connection_manager
|
146
|
+
dispatcher.connection_manager
|
147
|
+
end
|
148
|
+
|
149
149
|
def close_connection
|
150
150
|
@data_store.destroy!
|
151
151
|
@ping_timer.try(:cancel)
|
152
152
|
dispatcher.connection_manager.close_connection self
|
153
|
-
|
153
|
+
end
|
154
|
+
|
155
|
+
def current_user_responds_to?(identifier)
|
156
|
+
controller_delegate &&
|
157
|
+
controller_delegate.respond_to?(:current_user) &&
|
158
|
+
controller_delegate.current_user &&
|
159
|
+
controller_delegate.current_user.respond_to?(identifier)
|
154
160
|
end
|
155
161
|
|
156
162
|
attr_accessor :pong
|
@@ -171,6 +177,5 @@ module WebsocketRails
|
|
171
177
|
end
|
172
178
|
|
173
179
|
end
|
174
|
-
|
175
180
|
end
|
176
181
|
end
|
@@ -15,8 +15,8 @@ module WebsocketRails
|
|
15
15
|
BadRequestResponse = [400,{'Content-Type' => 'text/plain'},['invalid']].freeze
|
16
16
|
ExceptionResponse = [500,{'Content-Type' => 'text/plain'},['exception']].freeze
|
17
17
|
|
18
|
-
# Contains
|
19
|
-
# @return [
|
18
|
+
# Contains a Hash of currently open connections.
|
19
|
+
# @return [Hash]
|
20
20
|
attr_reader :connections
|
21
21
|
|
22
22
|
# Contains the {Dispatcher} instance for the active server.
|
@@ -28,7 +28,7 @@ module WebsocketRails
|
|
28
28
|
attr_reader :synchronization
|
29
29
|
|
30
30
|
def initialize
|
31
|
-
@connections =
|
31
|
+
@connections = {}
|
32
32
|
@dispatcher = Dispatcher.new(self)
|
33
33
|
|
34
34
|
if WebsocketRails.synchronize?
|
@@ -63,31 +63,57 @@ module WebsocketRails
|
|
63
63
|
private
|
64
64
|
|
65
65
|
def parse_incoming_event(params)
|
66
|
-
connection = find_connection_by_id
|
66
|
+
connection = find_connection_by_id(params["client_id"].to_i)
|
67
67
|
connection.on_message params["data"]
|
68
68
|
SuccessfulResponse
|
69
69
|
end
|
70
70
|
|
71
71
|
def find_connection_by_id(id)
|
72
|
-
connections
|
72
|
+
connections[id] || raise(InvalidConnectionError)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Opens a persistent connection using the appropriate {ConnectionAdapter}. Stores
|
76
|
-
# active connections in the {connections}
|
76
|
+
# active connections in the {connections} Hash.
|
77
77
|
def open_connection(request)
|
78
|
-
connection = ConnectionAdapters.establish_connection(
|
79
|
-
|
78
|
+
connection = ConnectionAdapters.establish_connection(request, dispatcher)
|
79
|
+
|
80
|
+
assign_connection_id connection
|
81
|
+
register_user_connection connection
|
82
|
+
|
83
|
+
connections[connection.id] = connection
|
84
|
+
|
80
85
|
info "Connection opened: #{connection}"
|
81
86
|
connection.rack_response
|
82
87
|
end
|
83
88
|
|
84
89
|
def close_connection(connection)
|
85
90
|
WebsocketRails.channel_manager.unsubscribe connection
|
86
|
-
|
91
|
+
destroy_user_connection connection
|
92
|
+
|
93
|
+
connections.delete connection.id
|
94
|
+
|
87
95
|
info "Connection closed: #{connection}"
|
88
96
|
connection = nil
|
89
97
|
end
|
90
98
|
public :close_connection
|
91
99
|
|
100
|
+
def assign_connection_id(connection)
|
101
|
+
begin
|
102
|
+
id = SecureRandom.hex(10)
|
103
|
+
end while connections.has_key?(id)
|
104
|
+
|
105
|
+
connection.id = id
|
106
|
+
end
|
107
|
+
|
108
|
+
def register_user_connection(connection)
|
109
|
+
return unless connection.user_connection?
|
110
|
+
WebsocketRails.users[connection.user_identifier] = connection
|
111
|
+
end
|
112
|
+
|
113
|
+
def destroy_user_connection(connection)
|
114
|
+
return unless connection.user_connection?
|
115
|
+
WebsocketRails.users.delete(connection)
|
116
|
+
end
|
117
|
+
|
92
118
|
end
|
93
119
|
end
|
@@ -115,7 +115,7 @@ module WebsocketRails
|
|
115
115
|
@namespace = validate_namespace( options[:namespace] || namespace )
|
116
116
|
end
|
117
117
|
|
118
|
-
def
|
118
|
+
def as_json
|
119
119
|
[
|
120
120
|
encoded_name,
|
121
121
|
{
|
@@ -127,7 +127,11 @@ module WebsocketRails
|
|
127
127
|
:result => result,
|
128
128
|
:server_token => server_token
|
129
129
|
}
|
130
|
-
]
|
130
|
+
]
|
131
|
+
end
|
132
|
+
|
133
|
+
def serialize
|
134
|
+
as_json.to_json
|
131
135
|
end
|
132
136
|
|
133
137
|
def is_channel?
|
@@ -5,6 +5,22 @@ require "redis/connection/ruby"
|
|
5
5
|
module WebsocketRails
|
6
6
|
class Synchronization
|
7
7
|
|
8
|
+
def self.all_users
|
9
|
+
singleton.all_users
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.find_user(connection)
|
13
|
+
singleton.find_user connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_user(connection)
|
17
|
+
singleton.register_user connection
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.destroy_user(connection)
|
21
|
+
singleton.destroy_user connection
|
22
|
+
end
|
23
|
+
|
8
24
|
def self.publish(event)
|
9
25
|
singleton.publish event
|
10
26
|
end
|
@@ -48,7 +64,7 @@ module WebsocketRails
|
|
48
64
|
|
49
65
|
def synchronize!
|
50
66
|
unless @synchronizing
|
51
|
-
@server_token =
|
67
|
+
@server_token = generate_server_token
|
52
68
|
register_server(@server_token)
|
53
69
|
|
54
70
|
synchro = Fiber.new do
|
@@ -94,7 +110,7 @@ module WebsocketRails
|
|
94
110
|
when event.is_channel?
|
95
111
|
WebsocketRails[event.channel].trigger_event(event)
|
96
112
|
when event.is_user?
|
97
|
-
connection = WebsocketRails.users[event.user_id]
|
113
|
+
connection = WebsocketRails.users[event.user_id.to_s]
|
98
114
|
return if connection.nil?
|
99
115
|
connection.trigger event
|
100
116
|
end
|
@@ -104,7 +120,7 @@ module WebsocketRails
|
|
104
120
|
remove_server(server_token)
|
105
121
|
end
|
106
122
|
|
107
|
-
def
|
123
|
+
def generate_server_token
|
108
124
|
begin
|
109
125
|
token = SecureRandom.urlsafe_base64
|
110
126
|
end while redis.sismember("websocket_rails.active_servers", token)
|
@@ -125,5 +141,34 @@ module WebsocketRails
|
|
125
141
|
EM.stop
|
126
142
|
end
|
127
143
|
|
144
|
+
def register_user(connection)
|
145
|
+
Fiber.new do
|
146
|
+
id = connection.user_identifier
|
147
|
+
user = connection.user
|
148
|
+
redis.hset 'websocket_rails.users', id, user.as_json(root: false).to_json
|
149
|
+
end.resume
|
150
|
+
end
|
151
|
+
|
152
|
+
def destroy_user(identifier)
|
153
|
+
Fiber.new do
|
154
|
+
redis.hdel 'websocket_rails.users', identifier
|
155
|
+
end.resume
|
156
|
+
end
|
157
|
+
|
158
|
+
def find_user(identifier)
|
159
|
+
Fiber.new do
|
160
|
+
redis_client = EM.reactor_running? ? redis : ruby_redis
|
161
|
+
raw_user = redis_client.hget('websocket_rails.users', identifier)
|
162
|
+
raw_user ? JSON.parse(raw_user) : nil
|
163
|
+
end.resume
|
164
|
+
end
|
165
|
+
|
166
|
+
def all_users
|
167
|
+
Fiber.new do
|
168
|
+
redis_client = EM.reactor_running? ? redis : ruby_redis
|
169
|
+
redis_client.hgetall('websocket_rails.users')
|
170
|
+
end.resume
|
171
|
+
end
|
172
|
+
|
128
173
|
end
|
129
174
|
end
|
@@ -27,8 +27,8 @@ module WebsocketRails
|
|
27
27
|
#
|
28
28
|
# If no `current_user` method is defined or the
|
29
29
|
# user is not signed in when the WebsocketRails
|
30
|
-
# connection is opened, the
|
31
|
-
#
|
30
|
+
# connection is opened, the connection will not be
|
31
|
+
# stored in the UserManager.
|
32
32
|
def self.users
|
33
33
|
@user_manager ||= UserManager.new
|
34
34
|
end
|
@@ -42,37 +42,228 @@ module WebsocketRails
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def [](identifier)
|
45
|
-
unless user = @users[identifier]
|
46
|
-
user =
|
45
|
+
unless user = (@users[identifier.to_s] || find_remote_user(identifier.to_s))
|
46
|
+
user = MissingConnection.new(identifier.to_s)
|
47
47
|
end
|
48
48
|
user
|
49
49
|
end
|
50
50
|
|
51
51
|
def []=(identifier, connection)
|
52
|
-
@users[identifier]
|
52
|
+
@users[identifier.to_s] ||= LocalConnection.new
|
53
|
+
@users[identifier.to_s] << connection
|
54
|
+
Synchronization.register_user(connection) if WebsocketRails.synchronize?
|
53
55
|
end
|
54
56
|
|
55
|
-
def delete(
|
56
|
-
|
57
|
+
def delete(connection)
|
58
|
+
identifier = connection.user_identifier.to_s
|
59
|
+
|
60
|
+
if (@users.has_key?(identifier) && @users[identifier].connections.count > 1)
|
61
|
+
@users[identifier].delete(connection)
|
62
|
+
else
|
63
|
+
@users.delete(identifier)
|
64
|
+
Synchronization.destroy_user(identifier) if WebsocketRails.synchronize?
|
65
|
+
end
|
57
66
|
end
|
58
67
|
|
59
|
-
|
68
|
+
# Behaves similarly to Ruby's Array#each, yielding each connection
|
69
|
+
# object stored in the {UserManager}. If synchronization is enabled,
|
70
|
+
# each connection from every active worker will be yielded.
|
71
|
+
#
|
72
|
+
# You can access the `current_user` object through the #user method.
|
73
|
+
#
|
74
|
+
# You can trigger an event on this user using the #send_message method
|
75
|
+
# which behaves identically to BaseController#send_message.
|
76
|
+
#
|
77
|
+
# If Synchronization is enabled, the state of the `current_user` object
|
78
|
+
# will be equivalent to it's state at the time the connection was opened.
|
79
|
+
# It will not reflect changes made after the connection has been opened.
|
80
|
+
def each(&block)
|
81
|
+
if WebsocketRails.synchronize?
|
82
|
+
users_hash = Synchronization.all_users || return
|
83
|
+
users_hash.each do |identifier, user_json|
|
84
|
+
connection = remote_connection_from_json(identifier, user_json)
|
85
|
+
block.call(connection) if block
|
86
|
+
end
|
87
|
+
else
|
88
|
+
users.each do |_, connection|
|
89
|
+
block.call(connection) if block
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
60
93
|
|
61
|
-
|
62
|
-
|
94
|
+
# Behaves similarly to Ruby's Array#map, invoking the given block with
|
95
|
+
# each active connection object and returning a new array with the results.
|
96
|
+
#
|
97
|
+
# See UserManager#each for details on the current usage and limitations.
|
98
|
+
def map(&block)
|
99
|
+
collection = []
|
100
|
+
|
101
|
+
each do |connection|
|
102
|
+
collection << block.call(connection) if block
|
103
|
+
end
|
104
|
+
|
105
|
+
collection
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def find_remote_user(identifier)
|
111
|
+
return unless WebsocketRails.synchronize?
|
112
|
+
user_hash = Synchronization.find_user(identifier) || return
|
113
|
+
|
114
|
+
remote_connection identifier, user_hash
|
115
|
+
end
|
116
|
+
|
117
|
+
def remote_connection_from_json(identifier, user_json)
|
118
|
+
user_hash = JSON.parse(user_json)
|
119
|
+
remote_connection identifier, user_hash
|
120
|
+
end
|
121
|
+
|
122
|
+
def remote_connection(identifier, user_hash)
|
123
|
+
RemoteConnection.new identifier, user_hash
|
124
|
+
end
|
125
|
+
|
126
|
+
# The UserManager::LocalConnection Class serves as a proxy object
|
127
|
+
# for storing multiple connections that belong to the same
|
128
|
+
# user. It implements the same basic interface as a Connection.
|
129
|
+
# This allows you to work with the object as though it is a
|
130
|
+
# single connection, but still trigger the events on all
|
131
|
+
# active connections belonging to the user.
|
132
|
+
class LocalConnection
|
133
|
+
|
134
|
+
attr_reader :connections
|
135
|
+
|
136
|
+
def initialize
|
137
|
+
@connections = []
|
138
|
+
end
|
139
|
+
|
140
|
+
def <<(connection)
|
141
|
+
@connections << connection
|
142
|
+
end
|
143
|
+
|
144
|
+
def delete(connection)
|
145
|
+
@connections.delete(connection)
|
146
|
+
end
|
147
|
+
|
148
|
+
def connected?
|
149
|
+
true
|
150
|
+
end
|
151
|
+
|
152
|
+
def user_identifier
|
153
|
+
latest_connection.user_identifier
|
154
|
+
end
|
155
|
+
|
156
|
+
def user
|
157
|
+
latest_connection.user
|
158
|
+
end
|
159
|
+
|
160
|
+
def trigger(event)
|
161
|
+
connections.each do |connection|
|
162
|
+
connection.trigger event
|
163
|
+
end
|
63
164
|
end
|
64
165
|
|
65
166
|
def send_message(event_name, data = {}, options = {})
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
false
|
167
|
+
options.merge! :user_id => user_identifier
|
168
|
+
options[:data] = data
|
169
|
+
|
170
|
+
event = Event.new(event_name, options)
|
171
|
+
|
172
|
+
# Trigger the event on all active connections for this user.
|
173
|
+
connections.each do |connection|
|
174
|
+
connection.trigger event
|
75
175
|
end
|
176
|
+
|
177
|
+
# Still publish the event in case the user is connected to
|
178
|
+
# other workers as well.
|
179
|
+
Synchronization.publish event if WebsocketRails.synchronize?
|
180
|
+
true
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def latest_connection
|
186
|
+
@connections.last
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
class RemoteConnection
|
192
|
+
|
193
|
+
attr_reader :user_identifier, :user
|
194
|
+
|
195
|
+
def initialize(identifier, user_hash)
|
196
|
+
@user_identifier = identifier.to_s
|
197
|
+
@user_hash = user_hash
|
198
|
+
end
|
199
|
+
|
200
|
+
def connected?
|
201
|
+
true
|
202
|
+
end
|
203
|
+
|
204
|
+
def user
|
205
|
+
@user ||= load_user
|
206
|
+
end
|
207
|
+
|
208
|
+
def send_message(event_name, data = {}, options = {})
|
209
|
+
options.merge! :user_id => @user_identifier
|
210
|
+
options[:data] = data
|
211
|
+
|
212
|
+
event = Event.new(event_name, options)
|
213
|
+
|
214
|
+
# If the user is connected to this worker, trigger the event
|
215
|
+
# immediately as the event will be ignored by the Synchronization
|
216
|
+
## dispatcher since the server_token will match.
|
217
|
+
if connection = WebsocketRails.users.users[@user_identifier]
|
218
|
+
connection.trigger event
|
219
|
+
end
|
220
|
+
|
221
|
+
# Still publish the event in case the user is connected to
|
222
|
+
# other workers as well.
|
223
|
+
#
|
224
|
+
# No need to check for Synchronization being enabled here.
|
225
|
+
# If a RemoteConnection has been fetched, Synchronization
|
226
|
+
# must be enabled.
|
227
|
+
Synchronization.publish event
|
228
|
+
true
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
def load_user
|
234
|
+
user = WebsocketRails.config.user_class.new
|
235
|
+
set_user_attributes user, @user_hash
|
236
|
+
user
|
237
|
+
end
|
238
|
+
|
239
|
+
def set_user_attributes(user, attr)
|
240
|
+
attr.each do |k, v|
|
241
|
+
user.send "#{k}=", v
|
242
|
+
end
|
243
|
+
user.instance_variable_set(:@new_record, false)
|
244
|
+
user.instance_variable_set(:@destroyed, false)
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
class MissingConnection
|
250
|
+
|
251
|
+
attr_reader :identifier
|
252
|
+
|
253
|
+
def initialize(identifier)
|
254
|
+
@user_identifier = identifier.to_s
|
255
|
+
end
|
256
|
+
|
257
|
+
def connected?
|
258
|
+
false
|
259
|
+
end
|
260
|
+
|
261
|
+
def user
|
262
|
+
nil
|
263
|
+
end
|
264
|
+
|
265
|
+
def send_message(*args)
|
266
|
+
false
|
76
267
|
end
|
77
268
|
|
78
269
|
def nil?
|