websocket-rails 0.5.0 → 0.6.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 +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?
|