celluloid_pubsub 0.3.3 → 0.4.1
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.
- checksums.yaml +4 -4
- data/lib/celluloid_pubsub/client.rb +29 -23
- data/lib/celluloid_pubsub/helper.rb +47 -11
- data/lib/celluloid_pubsub/reactor.rb +48 -4
- data/lib/celluloid_pubsub/redis_reactor.rb +274 -0
- data/lib/celluloid_pubsub/version.rb +2 -2
- data/lib/celluloid_pubsub/web_server.rb +67 -49
- data/spec/lib/celluloid_pubsub/reactor_spec.rb +2 -2
- metadata +2 -3
- data/lib/celluloid_pubsub/redis/redis.rb +0 -38
- data/lib/celluloid_pubsub/redis/redis_reactor.rb +0 -96
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef1607ef65dd326efbf983675c21f745babab415
|
4
|
+
data.tar.gz: fbcfed23005b8d5b22232396fb5106ef13b69877
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 081ee9751029919561347195180494667039584a58d71bb4b2d107582b9f26af0e9b8001bb7bcdae6e86ab2d150c1c4bd4b876879ad9797d8762e130c131fbd5
|
7
|
+
data.tar.gz: 64492820b1123719845b670a22d58314164604319a341af2d7f24c27e015f55f3a75b1da28f4a1d2c270449f4e008b76970cf8cee17752ff5154ecfbee89cf69
|
@@ -7,41 +7,29 @@ module CelluloidPubsub
|
|
7
7
|
# @!attribute actor
|
8
8
|
# @return [Celluloid::Actor] actor to which callbacks will be delegated to
|
9
9
|
#
|
10
|
-
# @!attribute connect_blk
|
11
|
-
# @return [Proc] Block that will execute after the connection is opened
|
12
|
-
#
|
13
|
-
# @!attribute connection
|
14
|
-
# @return [Celluloid::WebSocket::Client] A websocket connection that is used to chat witht the webserver
|
15
|
-
#
|
16
10
|
# @!attribute options
|
17
11
|
# @return [Hash] the options that can be used to connect to webser and send additional data
|
18
12
|
#
|
19
|
-
# @!attribute
|
20
|
-
# @return [String] The
|
21
|
-
#
|
22
|
-
# @!attribute port
|
23
|
-
# @return [String] The port on which the webserver runs on
|
24
|
-
#
|
25
|
-
# @!attribute path
|
26
|
-
# @return [String] The hostname on which the webserver runs on
|
13
|
+
# @!attribute channel
|
14
|
+
# @return [String] The channel to which the client will subscribe to
|
27
15
|
class Client
|
28
16
|
include Celluloid
|
29
17
|
include Celluloid::Logger
|
30
18
|
include CelluloidPubsub::Helper
|
31
19
|
|
32
|
-
|
20
|
+
attr_accessor :actor, :options, :channel
|
33
21
|
finalizer :shutdown
|
34
22
|
# receives a list of options that are used to connect to the webserver and an actor to which the callbacks are delegated to
|
35
23
|
# when receiving messages from a channel
|
36
24
|
#
|
37
25
|
# @param [Hash] options the options that can be used to connect to webser and send additional data
|
38
26
|
# @option options [String] :actor The actor that made the connection
|
27
|
+
# @option options [String] :channel The channel to which the client will subscribe to once the connection is open
|
28
|
+
# @option options [String] :log_file_path The path to the log file where debug messages will be printed, otherwise will use STDOUT
|
39
29
|
# @option options [String]:hostname The hostname on which the webserver runs on
|
40
30
|
# @option options [String] :port The port on which the webserver runs on
|
41
31
|
# @option options [String] :path The request path that the webserver accepts
|
42
32
|
#
|
43
|
-
# @param [Proc] connect_blk Block that will execute after the connection is opened
|
44
|
-
#
|
45
33
|
# @return [void]
|
46
34
|
#
|
47
35
|
# @api public
|
@@ -50,11 +38,15 @@ module CelluloidPubsub
|
|
50
38
|
@actor ||= @options.fetch('actor', nil)
|
51
39
|
@channel ||= @options.fetch('channel', nil)
|
52
40
|
raise "#{self}: Please provide an actor in the options list!!!" if @actor.blank?
|
53
|
-
raise "#{self}: Please provide an channel in the options list!!!" if @channel.blank?
|
54
41
|
supervise_actors
|
55
42
|
setup_celluloid_logger
|
56
43
|
end
|
57
44
|
|
45
|
+
# the method will return the path to the log file where debug messages will be printed
|
46
|
+
#
|
47
|
+
# @return [String, nil] return the path to the log file where debug messages will be printed
|
48
|
+
#
|
49
|
+
# @api public
|
58
50
|
def log_file_path
|
59
51
|
@log_file_path ||= @options.fetch('log_file_path', nil)
|
60
52
|
end
|
@@ -80,14 +72,32 @@ module CelluloidPubsub
|
|
80
72
|
@connection ||= Celluloid::WebSocket::Client.new("ws://#{hostname}:#{port}#{path}", Actor.current)
|
81
73
|
end
|
82
74
|
|
75
|
+
# the method will return the hostname of the server
|
76
|
+
#
|
77
|
+
#
|
78
|
+
# @return [String] the hostname where the server runs on
|
79
|
+
#
|
80
|
+
# @api public
|
83
81
|
def hostname
|
84
82
|
@hostname ||= @options.fetch('hostname', CelluloidPubsub::WebServer::HOST)
|
85
83
|
end
|
86
84
|
|
85
|
+
# the method will return the port on which the server accepts connections
|
86
|
+
#
|
87
|
+
#
|
88
|
+
# @return [String] the port on which the server accepts connections
|
89
|
+
#
|
90
|
+
# @api public
|
87
91
|
def port
|
88
92
|
@port ||= @options.fetch('port', CelluloidPubsub::WebServer::PORT)
|
89
93
|
end
|
90
94
|
|
95
|
+
# the method will return the path of the URL on which the servers acccepts the connection
|
96
|
+
#
|
97
|
+
#
|
98
|
+
# @return [String] the URL path that the server is mounted on
|
99
|
+
#
|
100
|
+
# @api public
|
91
101
|
def path
|
92
102
|
@path ||= @options.fetch('path', CelluloidPubsub::WebServer::PATH)
|
93
103
|
end
|
@@ -175,11 +185,7 @@ module CelluloidPubsub
|
|
175
185
|
# @api public
|
176
186
|
def on_open
|
177
187
|
log_debug("#{@actor.class} websocket connection opened")
|
178
|
-
async.subscribe(@channel)
|
179
|
-
end
|
180
|
-
|
181
|
-
def log_debug(message)
|
182
|
-
debug message if debug_enabled?
|
188
|
+
async.subscribe(@channel) if @channel.present?
|
183
189
|
end
|
184
190
|
|
185
191
|
# callback executes when actor receives a message from a subscribed channel
|
@@ -3,9 +3,9 @@ module CelluloidPubsub
|
|
3
3
|
module Helper
|
4
4
|
# checks if the message has the successfull subscription action
|
5
5
|
#
|
6
|
-
# @param [string] message
|
6
|
+
# @param [string] message The message that will be checked
|
7
7
|
#
|
8
|
-
# @return [
|
8
|
+
# @return [Boolean] return true if message contains key client_action with value 'succesfull_subscription'
|
9
9
|
#
|
10
10
|
# @api public
|
11
11
|
def succesfull_subscription?(message)
|
@@ -14,6 +14,21 @@ module CelluloidPubsub
|
|
14
14
|
|
15
15
|
module_function
|
16
16
|
|
17
|
+
# method used to determine if a action is a subsribe action
|
18
|
+
# @param [string] action The action that will be checked
|
19
|
+
#
|
20
|
+
# @return [Boolean] Returns true if the action equals to 'subscribe'
|
21
|
+
#
|
22
|
+
# @api public
|
23
|
+
def action_subscribe?(action)
|
24
|
+
action == 'subscribe'
|
25
|
+
end
|
26
|
+
|
27
|
+
# sets the celluloid logger and the celluloid exception handler
|
28
|
+
#
|
29
|
+
# @return [void]
|
30
|
+
#
|
31
|
+
# @api private
|
17
32
|
def setup_celluloid_logger
|
18
33
|
return if !debug_enabled? || (respond_to?(:log_file_path) && log_file_path.blank?)
|
19
34
|
setup_log_file
|
@@ -21,6 +36,11 @@ module CelluloidPubsub
|
|
21
36
|
setup_celluloid_exception_handler
|
22
37
|
end
|
23
38
|
|
39
|
+
# sets the celluloid exception handler
|
40
|
+
#
|
41
|
+
# @return [void]
|
42
|
+
#
|
43
|
+
# @api private
|
24
44
|
def setup_celluloid_exception_handler
|
25
45
|
Celluloid.task_class = Celluloid::TaskThread
|
26
46
|
Celluloid.exception_handler do |ex|
|
@@ -28,6 +48,11 @@ module CelluloidPubsub
|
|
28
48
|
end
|
29
49
|
end
|
30
50
|
|
51
|
+
# creates the log file where the debug messages will be printed
|
52
|
+
#
|
53
|
+
# @return [void]
|
54
|
+
#
|
55
|
+
# @api private
|
31
56
|
def setup_log_file
|
32
57
|
return if !debug_enabled? || (respond_to?(:log_file_path) && log_file_path.blank?)
|
33
58
|
FileUtils.mkdir_p(File.dirname(log_file_path)) unless File.directory?(log_file_path)
|
@@ -35,28 +60,39 @@ module CelluloidPubsub
|
|
35
60
|
log_file.sync = true
|
36
61
|
end
|
37
62
|
|
63
|
+
# checks if a given error needs to be filtered
|
64
|
+
#
|
65
|
+
# @param [Exception::Error] error
|
66
|
+
#
|
67
|
+
# @return [Boolean] returns true if the error should be filtered otherwise false
|
68
|
+
#
|
69
|
+
# @api private
|
38
70
|
def filtered_error?(error)
|
39
71
|
[Interrupt].any? { |class_name| error.is_a?(class_name) }
|
40
72
|
end
|
41
73
|
|
42
|
-
# receives a list of options that
|
74
|
+
# receives a list of options that need to be parsed
|
75
|
+
# if it is an Array will return the first element , otherwise if it is
|
76
|
+
# an Hash will return the hash with string keys, otherwise an empty hash
|
43
77
|
#
|
44
|
-
# @param [Hash] options the options that
|
45
|
-
# @option options [String]:hostname The hostname on which the webserver runs on
|
46
|
-
# @option options [Integer] :port The port on which the webserver runs on
|
47
|
-
# @option options [String] :path The request path that the webserver accepts
|
48
|
-
# @option options [Boolean] :spy Enable this only if you want to enable debugging for the webserver
|
49
|
-
# @option options [Integer]:backlog How many connections the server accepts
|
78
|
+
# @param [Hash, Array] options the options that need to be parsed
|
50
79
|
#
|
51
|
-
# @return [
|
80
|
+
# @return [Hash]
|
52
81
|
#
|
53
|
-
# @api
|
82
|
+
# @api private
|
54
83
|
def parse_options(options)
|
55
84
|
options = options.is_a?(Array) ? options.first : options
|
56
85
|
options = options.is_a?(Hash) ? options.stringify_keys : {}
|
57
86
|
options
|
58
87
|
end
|
59
88
|
|
89
|
+
# receives a message, and logs it to the log file if debug is enabled
|
90
|
+
#
|
91
|
+
# @param [Object] message
|
92
|
+
#
|
93
|
+
# @return [void]
|
94
|
+
#
|
95
|
+
# @api private
|
60
96
|
def log_debug(message)
|
61
97
|
debug message if respond_to?(:debug_enabled?) && debug_enabled?
|
62
98
|
end
|
@@ -36,6 +36,12 @@ module CelluloidPubsub
|
|
36
36
|
async.run
|
37
37
|
end
|
38
38
|
|
39
|
+
# the method will return true if debug is enabled
|
40
|
+
#
|
41
|
+
#
|
42
|
+
# @return [Boolean] returns true if debug is enabled otherwise false
|
43
|
+
#
|
44
|
+
# @api public
|
39
45
|
def debug_enabled?
|
40
46
|
@server.debug_enabled?
|
41
47
|
end
|
@@ -71,12 +77,16 @@ module CelluloidPubsub
|
|
71
77
|
nil
|
72
78
|
end
|
73
79
|
|
80
|
+
# the method will return the reactor's class name used in debug messages
|
81
|
+
#
|
82
|
+
#
|
83
|
+
# @return [Class] returns the reactor's class name used in debug messages
|
84
|
+
#
|
85
|
+
# @api public
|
74
86
|
def reactor_class
|
75
87
|
self.class
|
76
88
|
end
|
77
89
|
|
78
|
-
# :nocov:
|
79
|
-
|
80
90
|
# method used to parse a JSON object into a Hash object
|
81
91
|
#
|
82
92
|
# @param [JSON] message
|
@@ -145,7 +155,8 @@ module CelluloidPubsub
|
|
145
155
|
# @option json_data [String] :client_action The action based on which the reactor will decide what action should make
|
146
156
|
#
|
147
157
|
# Possible values are:
|
148
|
-
#
|
158
|
+
# unsubscribe_all
|
159
|
+
# unsubscribe_clients
|
149
160
|
# unsubscribe
|
150
161
|
# subscribe
|
151
162
|
# publish
|
@@ -166,7 +177,7 @@ module CelluloidPubsub
|
|
166
177
|
when 'subscribe'
|
167
178
|
async.start_subscriber(channel, json_data)
|
168
179
|
when 'publish'
|
169
|
-
|
180
|
+
async.publish_event(channel, json_data['data'].to_json)
|
170
181
|
else
|
171
182
|
handle_unknown_action(json_data)
|
172
183
|
end
|
@@ -185,6 +196,14 @@ module CelluloidPubsub
|
|
185
196
|
@server.async.handle_dispatched_message(Actor.current, json_data)
|
186
197
|
end
|
187
198
|
|
199
|
+
# if the reactor has unsubscribed from all his channels will close the websocket connection,
|
200
|
+
# otherwise will delete the channel from his channel list
|
201
|
+
#
|
202
|
+
# @param [String] channel The channel that needs to be deleted from the reactor's list of subscribed channels
|
203
|
+
#
|
204
|
+
# @return [void]
|
205
|
+
#
|
206
|
+
# @api public
|
188
207
|
def forget_channel(channel)
|
189
208
|
if @channels.blank?
|
190
209
|
@websocket.close
|
@@ -254,6 +273,14 @@ module CelluloidPubsub
|
|
254
273
|
@websocket << message.merge('client_action' => 'successful_subscription', 'channel' => channel).to_json unless @server.redis_enabled?
|
255
274
|
end
|
256
275
|
|
276
|
+
# this method will return a list of all subscribers to a particular channel or a empty array
|
277
|
+
#
|
278
|
+
#
|
279
|
+
# @param [String] channel The channel that will be used to fetch all subscribers from this channel
|
280
|
+
#
|
281
|
+
# @return [Array] returns a list of all subscribers to a particular channel or a empty array
|
282
|
+
#
|
283
|
+
# @api public
|
257
284
|
def channel_subscribers(channel)
|
258
285
|
@server.subscribers[channel] || []
|
259
286
|
end
|
@@ -274,6 +301,23 @@ module CelluloidPubsub
|
|
274
301
|
@server.subscribers[channel] = channel_subscribers(channel).push(reactor: Actor.current, message: message)
|
275
302
|
end
|
276
303
|
|
304
|
+
# method for publishing data to a channel
|
305
|
+
#
|
306
|
+
# @param [String] current_topic The Channel to which the reactor instance {CelluloidPubsub::Reactor} will publish the message to
|
307
|
+
# @param [Object] message
|
308
|
+
#
|
309
|
+
# @return [void]
|
310
|
+
#
|
311
|
+
# @api public
|
312
|
+
def publish_event(current_topic, message)
|
313
|
+
return if current_topic.blank? || message.blank?
|
314
|
+
(@server.subscribers[current_topic] || []).each do |hash|
|
315
|
+
hash[:reactor].websocket << message
|
316
|
+
end
|
317
|
+
rescue => exception
|
318
|
+
log_debug("could not publish message #{message} into topic #{current_topic} because of #{exception.inspect}")
|
319
|
+
end
|
320
|
+
|
277
321
|
# unsubscribes all actors from all channels and terminates the curent actor
|
278
322
|
#
|
279
323
|
# @return [void]
|
@@ -0,0 +1,274 @@
|
|
1
|
+
require_relative './reactor'
|
2
|
+
require_relative './helper'
|
3
|
+
module CelluloidPubsub
|
4
|
+
# reactor used for redis pubsub
|
5
|
+
# @!attribute connected
|
6
|
+
# @return [Boolean] returns true if already connected to redis
|
7
|
+
# @!attribute connection
|
8
|
+
# @return [EM::Hiredis] The connection used for redis
|
9
|
+
class RedisReactor < CelluloidPubsub::Reactor
|
10
|
+
include Celluloid
|
11
|
+
include Celluloid::IO
|
12
|
+
include Celluloid::Logger
|
13
|
+
include CelluloidPubsub::Helper
|
14
|
+
|
15
|
+
attr_accessor :connected, :connection
|
16
|
+
|
17
|
+
alias_method :connected?, :connected
|
18
|
+
|
19
|
+
# returns true if already connected to redis otherwise false
|
20
|
+
#
|
21
|
+
# @return [Boolean] returns true if already connected to redis otherwise false
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def connected
|
25
|
+
@connected ||= false
|
26
|
+
end
|
27
|
+
|
28
|
+
# method used to unsubscribe from a channel
|
29
|
+
# @see #redis_action
|
30
|
+
#
|
31
|
+
# @return [void]
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def unsubscribe(channel)
|
35
|
+
super
|
36
|
+
async.redis_action('unsubscribe', channel)
|
37
|
+
end
|
38
|
+
|
39
|
+
# method used to subscribe to a channel
|
40
|
+
# @see #redis_action
|
41
|
+
#
|
42
|
+
# @return [void]
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def add_subscriber_to_channel(channel, message)
|
46
|
+
super
|
47
|
+
async.redis_action('subscribe', channel, message)
|
48
|
+
end
|
49
|
+
|
50
|
+
# method used to unsubscribe from a channel
|
51
|
+
# @see #redis_action
|
52
|
+
#
|
53
|
+
# @return [void]
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
def unsubscribe_from_channel(channel)
|
57
|
+
super
|
58
|
+
async.redis_action('unsubscribe', channel)
|
59
|
+
end
|
60
|
+
|
61
|
+
# method used to unsubscribe from all channels
|
62
|
+
# @see #redis_action
|
63
|
+
#
|
64
|
+
# @return [void]
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def unsubscribe_all
|
68
|
+
@channels.each do |channel|
|
69
|
+
async.redis_action('unsubscribe', channel)
|
70
|
+
end
|
71
|
+
info 'clearing connections'
|
72
|
+
shutdown
|
73
|
+
end
|
74
|
+
|
75
|
+
# method used to shutdown the reactor and unsubscribe from all channels
|
76
|
+
# @see #redis_action
|
77
|
+
#
|
78
|
+
# @return [void]
|
79
|
+
#
|
80
|
+
# @api public
|
81
|
+
def shutdown
|
82
|
+
@channels.each do |channel|
|
83
|
+
redis_action('unsubscribe', channel)
|
84
|
+
end if @channels.present?
|
85
|
+
super
|
86
|
+
end
|
87
|
+
|
88
|
+
# method used to publish event using redis
|
89
|
+
#
|
90
|
+
# @return [void]
|
91
|
+
#
|
92
|
+
# @api public
|
93
|
+
def publish_event(topic, data)
|
94
|
+
return if topic.blank? || data.blank?
|
95
|
+
connect_to_redis do |connection|
|
96
|
+
connection.publish(topic, data)
|
97
|
+
end
|
98
|
+
rescue => exception
|
99
|
+
log_debug("could not publish message #{message} into topic #{current_topic} because of #{exception.inspect}")
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
# method used to run the enventmachine and setup the exception handler
|
105
|
+
# @see #run_the_eventmachine
|
106
|
+
# @see #setup_em_exception_handler
|
107
|
+
#
|
108
|
+
# @param [Proc] block the block that will use the connection
|
109
|
+
#
|
110
|
+
# @return [void]
|
111
|
+
#
|
112
|
+
# @api private
|
113
|
+
def connect_to_redis(&block)
|
114
|
+
require 'eventmachine'
|
115
|
+
require 'em-hiredis'
|
116
|
+
run_the_eventmachine(&block)
|
117
|
+
setup_em_exception_handler
|
118
|
+
end
|
119
|
+
|
120
|
+
# method used to connect to redis and yield the connection
|
121
|
+
#
|
122
|
+
# @param [Proc] block the block that will use the connection
|
123
|
+
#
|
124
|
+
# @return [void]
|
125
|
+
#
|
126
|
+
# @api private
|
127
|
+
def run_the_eventmachine(&block)
|
128
|
+
EM.run do
|
129
|
+
@connection ||= EM::Hiredis.connect
|
130
|
+
@connected = true
|
131
|
+
block.call @connection
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# method used to setup the eventmachine exception handler
|
136
|
+
#
|
137
|
+
# @return [void]
|
138
|
+
#
|
139
|
+
# @api private
|
140
|
+
def setup_em_exception_handler
|
141
|
+
EM.error_handler do |error|
|
142
|
+
debug error unless filtered_error?(error)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# method used to fetch the pubsub client from the connection and yield it
|
147
|
+
#
|
148
|
+
# @return [void]
|
149
|
+
#
|
150
|
+
# @api private
|
151
|
+
def fetch_pubsub
|
152
|
+
connect_to_redis do |connection|
|
153
|
+
@pubsub ||= connection.pubsub
|
154
|
+
yield @pubsub if block_given?
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# method used to fetch the pubsub client from the connection and yield it
|
159
|
+
# @see #action_subscribe
|
160
|
+
#
|
161
|
+
# @param [string] action The action that will be checked
|
162
|
+
# @param [string] channel The channel that reactor has subscribed to
|
163
|
+
# @param [string] message The initial message used to subscribe
|
164
|
+
#
|
165
|
+
# @return [void]
|
166
|
+
#
|
167
|
+
# @api private
|
168
|
+
def action_success(action, channel, message)
|
169
|
+
action_subscribe?(action) ? message.merge('client_action' => 'successful_subscription', 'channel' => channel) : nil
|
170
|
+
end
|
171
|
+
|
172
|
+
# method used execute an action (subscribe or unsubscribe ) to redis
|
173
|
+
# @see #prepare_redis_action
|
174
|
+
# @see #action_success
|
175
|
+
# @see #register_subscription_callbacks
|
176
|
+
#
|
177
|
+
# @param [string] action The action that will be checked
|
178
|
+
# @param [string] channel The channel that reactor has subscribed to
|
179
|
+
# @param [string] message The initial message used to subscribe
|
180
|
+
#
|
181
|
+
# @return [void]
|
182
|
+
#
|
183
|
+
# @api private
|
184
|
+
def redis_action(action, channel = nil, message = {})
|
185
|
+
fetch_pubsub do |pubsub|
|
186
|
+
callback = prepare_redis_action(action)
|
187
|
+
success_message = action_success(action, channel, message)
|
188
|
+
subscription = pubsub.send(action, channel, callback)
|
189
|
+
register_subscription_callbacks(subscription, action, success_message)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# method used check if the action is subscribe and write the incoming message to be websocket or log the message otherwise
|
194
|
+
# @see #log_unsubscriptions
|
195
|
+
# @see #action_subscribe
|
196
|
+
#
|
197
|
+
# @param [String] action The action that will be checked if it is subscribed
|
198
|
+
#
|
199
|
+
# @return [void]
|
200
|
+
#
|
201
|
+
# @api private
|
202
|
+
def prepare_redis_action(action)
|
203
|
+
log_unsubscriptions(pubsub)
|
204
|
+
proc do |subscribed_message|
|
205
|
+
action_subscribe?(action) ? (@websocket << subscribed_message) : log_debug(message)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# method used to listen to unsubscriptions and log them to log file
|
210
|
+
# @see #register_redis_callback
|
211
|
+
# @see #register_redis_error_callback
|
212
|
+
#
|
213
|
+
# @param [EM::Hiredis::PubsubClient] pubsub The pubsub client that will be used to listen to unsubscriptions
|
214
|
+
#
|
215
|
+
# @return [void]
|
216
|
+
#
|
217
|
+
# @api private
|
218
|
+
def log_unsubscriptions(pubsub)
|
219
|
+
pubsub.on(:unsubscribe) do |subscribed_channel, remaining_subscriptions|
|
220
|
+
log_debug [:unsubscribe_happened, subscribed_channel, remaining_subscriptions].inspect
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# method used registers the sucess and error callabacks
|
225
|
+
# @see #register_redis_callback
|
226
|
+
# @see #register_redis_error_callback
|
227
|
+
#
|
228
|
+
# @param [EM::DefaultDeferrable] subscription The subscription object
|
229
|
+
# @param [string] action The action that will be checked
|
230
|
+
# @param [string] sucess_message The initial message used to subscribe
|
231
|
+
#
|
232
|
+
# @return [void]
|
233
|
+
#
|
234
|
+
# @api private
|
235
|
+
def register_subscription_callbacks(subscription, action, sucess_message = nil)
|
236
|
+
register_redis_callback(subscription, sucess_message)
|
237
|
+
register_redis_error_callback(subscription, action)
|
238
|
+
end
|
239
|
+
|
240
|
+
# method used to register a success callback and if action is subscribe will write
|
241
|
+
# back to the websocket a message that will say it is a successful_subscription
|
242
|
+
# If action is something else, will log the incoming message
|
243
|
+
# @see #log_debug
|
244
|
+
#
|
245
|
+
# @param [EM::DefaultDeferrable] subscription The subscription object
|
246
|
+
# @param [string] sucess_message The initial message used to subscribe
|
247
|
+
#
|
248
|
+
# @return [void]
|
249
|
+
#
|
250
|
+
# @api private
|
251
|
+
def register_redis_callback(subscription, sucess_message = nil)
|
252
|
+
subscription.callback do |subscriptions_ids|
|
253
|
+
if sucess_message.present?
|
254
|
+
@websocket << sucess_message.merge('subscriptions' => subscriptions_ids).to_json
|
255
|
+
else
|
256
|
+
log_debug "#{action} success #{success_response.inspect}"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Register an error callback on the deferrable object and logs to file the incoming message
|
262
|
+
# @see #log_debug
|
263
|
+
#
|
264
|
+
# @param [EM::DefaultDeferrable] subscription The subscription object
|
265
|
+
# @param [string] action The action that will be checked
|
266
|
+
#
|
267
|
+
# @return [void]
|
268
|
+
#
|
269
|
+
# @api private
|
270
|
+
def register_redis_error_callback(subscription, action)
|
271
|
+
subscription.errback { |reply| log_debug "#{action} error #{reply.inspect}" }
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -13,22 +13,6 @@ module CelluloidPubsub
|
|
13
13
|
#
|
14
14
|
# @!attribute subscribers
|
15
15
|
# @return [Hash] The hostname on which the webserver runs on
|
16
|
-
#
|
17
|
-
# @!attribute backlog
|
18
|
-
# @return [Integer] Determines how many connections can be used
|
19
|
-
# Defaults to 1024
|
20
|
-
#
|
21
|
-
# @!attribute hostname
|
22
|
-
# @return [String] The hostname on which the webserver runs on
|
23
|
-
#
|
24
|
-
# @!attribute port
|
25
|
-
# @return [String] The port on which the webserver runs on
|
26
|
-
#
|
27
|
-
# @!attribute path
|
28
|
-
# @return [String] The hostname on which the webserver runs on
|
29
|
-
#
|
30
|
-
# @!attribute spy
|
31
|
-
# @return [Boolean] Enable this only if you want to enable debugging for the webserver
|
32
16
|
class WebServer < Reel::Server::HTTP
|
33
17
|
include Celluloid::Logger
|
34
18
|
include CelluloidPubsub::Helper
|
@@ -40,7 +24,7 @@ module CelluloidPubsub
|
|
40
24
|
# The request path that the webserver accepts by default
|
41
25
|
PATH = '/ws'
|
42
26
|
|
43
|
-
attr_accessor :options, :subscribers
|
27
|
+
attr_accessor :options, :subscribers
|
44
28
|
finalizer :shutdown
|
45
29
|
# receives a list of options that are used to configure the webserver
|
46
30
|
#
|
@@ -64,10 +48,22 @@ module CelluloidPubsub
|
|
64
48
|
super(hostname, port, { spy: spy, backlog: backlog }, &method(:on_connection))
|
65
49
|
end
|
66
50
|
|
51
|
+
# the method will return true if redis can be used otherwise false
|
52
|
+
#
|
53
|
+
#
|
54
|
+
# @return [Boolean] return true if redis can be used otherwise false
|
55
|
+
#
|
56
|
+
# @api public
|
67
57
|
def use_redis
|
68
58
|
@use_redis ||= @options.fetch('use_redis', false)
|
69
59
|
end
|
70
60
|
|
61
|
+
# the method will return true if debug is enabled otherwise false
|
62
|
+
#
|
63
|
+
#
|
64
|
+
# @return [Boolean] returns true if debug is enabled otherwise false
|
65
|
+
#
|
66
|
+
# @api public
|
71
67
|
def debug_enabled?
|
72
68
|
@debug_enabled = @options.fetch('enable_debug', false)
|
73
69
|
@debug_enabled == true
|
@@ -84,66 +80,74 @@ module CelluloidPubsub
|
|
84
80
|
terminate
|
85
81
|
end
|
86
82
|
|
83
|
+
# the method will return the file path of the log file where debug messages will be printed
|
84
|
+
#
|
85
|
+
#
|
86
|
+
# @return [String] returns the file path of the log file where debug messages will be printed
|
87
|
+
#
|
88
|
+
# @api public
|
87
89
|
def log_file_path
|
88
90
|
@log_file_path ||= @options.fetch('log_file_path', nil)
|
89
91
|
end
|
90
92
|
|
93
|
+
# the method will return the hostname on which the server is running on
|
94
|
+
#
|
95
|
+
#
|
96
|
+
# @return [String] returns the hostname on which the server is running on
|
97
|
+
#
|
98
|
+
# @api public
|
91
99
|
def hostname
|
92
100
|
@hostname ||= @options.fetch('hostname', CelluloidPubsub::WebServer::HOST)
|
93
101
|
end
|
94
102
|
|
103
|
+
# the method will return the port on which will accept connections
|
104
|
+
#
|
105
|
+
#
|
106
|
+
# @return [String] returns the port on which will accept connections
|
107
|
+
#
|
108
|
+
# @api public
|
95
109
|
def port
|
96
110
|
@port ||= @options.fetch('port', CelluloidPubsub::WebServer::PORT)
|
97
111
|
end
|
98
112
|
|
113
|
+
# the method will return the URL path on which will acceept connections
|
114
|
+
#
|
115
|
+
#
|
116
|
+
# @return [String] returns the URL path on which will acceept connections
|
117
|
+
#
|
118
|
+
# @api public
|
99
119
|
def path
|
100
120
|
@path ||= @options.fetch('path', CelluloidPubsub::WebServer::PATH)
|
101
121
|
end
|
102
122
|
|
123
|
+
# the method will return true if connection to the server should be spied upon
|
124
|
+
#
|
125
|
+
#
|
126
|
+
# @return [Boolean] returns true if connection to the server should be spied upon, otherwise false
|
127
|
+
#
|
128
|
+
# @api public
|
103
129
|
def spy
|
104
130
|
@spy ||= @options.fetch('spy', false)
|
105
131
|
end
|
106
132
|
|
133
|
+
# the method will return the number of connections allowed to the server
|
134
|
+
#
|
135
|
+
#
|
136
|
+
# @return [Integer] returns the number of connections allowed to the server
|
137
|
+
#
|
138
|
+
# @api public
|
107
139
|
def backlog
|
108
140
|
@backlog = @options.fetch('backlog', 1024)
|
109
141
|
end
|
110
142
|
|
111
|
-
#
|
112
|
-
|
113
|
-
def redis_enabled?
|
114
|
-
use_redis.to_s.downcase == 'true'
|
115
|
-
end
|
116
|
-
|
117
|
-
# method for publishing data to a channel
|
143
|
+
# the method will return true if redis is enabled otherwise false
|
118
144
|
#
|
119
|
-
# @param [String] current_topic The Channel to which the reactor instance {CelluloidPubsub::Rector} will publish the message to
|
120
|
-
# @param [Object] message
|
121
145
|
#
|
122
|
-
# @return [
|
146
|
+
# @return [Boolean] returns true if redis is enabled otherwise false
|
123
147
|
#
|
124
148
|
# @api public
|
125
|
-
def
|
126
|
-
|
127
|
-
publish_redis_event(current_topic, message)
|
128
|
-
else
|
129
|
-
publish_clasic_event(current_topic, message)
|
130
|
-
end
|
131
|
-
rescue => exception
|
132
|
-
log_debug("could not publish message #{message} into topic #{current_topic} because of #{exception.inspect}")
|
133
|
-
end
|
134
|
-
|
135
|
-
def publish_redis_event(topic, data)
|
136
|
-
return if !redis_enabled? || topic.blank? || data.blank?
|
137
|
-
CelluloidPubsub::Redis.connect do |connection|
|
138
|
-
connection.publish(topic, data)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def publish_clasic_event(channel, data)
|
143
|
-
return if channel.blank? || data.blank?
|
144
|
-
(@subscribers[channel] || []).each do |hash|
|
145
|
-
hash[:reactor].websocket << data
|
146
|
-
end
|
149
|
+
def redis_enabled?
|
150
|
+
use_redis.to_s.downcase == 'true'
|
147
151
|
end
|
148
152
|
|
149
153
|
# callback that will execute when receiving new conections
|
@@ -180,10 +184,24 @@ module CelluloidPubsub
|
|
180
184
|
end
|
181
185
|
end
|
182
186
|
|
187
|
+
# returns the reactor class that will handle the connection depending if redis is enabled or not
|
188
|
+
# @see #redis_enabled?
|
189
|
+
#
|
190
|
+
# @return [Class] returns the reactor class that will handle the connection depending if redis is enabled or not
|
191
|
+
#
|
192
|
+
# @api public
|
183
193
|
def reactor_class
|
184
194
|
redis_enabled? ? CelluloidPubsub::RedisReactor : CelluloidPubsub::Reactor
|
185
195
|
end
|
186
196
|
|
197
|
+
# method will instantiate a new reactor object, will link the reactor to the current actor and will dispatch the request to the reactor
|
198
|
+
# @see #route_websocket
|
199
|
+
#
|
200
|
+
# @param [Reel::WebSocket] request The request that was made to the webserver
|
201
|
+
#
|
202
|
+
# @return [void]
|
203
|
+
#
|
204
|
+
# @api public
|
187
205
|
def dispatch_websocket_request(request)
|
188
206
|
reactor = reactor_class.new
|
189
207
|
Actor.current.link reactor
|
@@ -110,7 +110,7 @@ describe CelluloidPubsub::Reactor do
|
|
110
110
|
|
111
111
|
it 'publish' do
|
112
112
|
data = { 'client_action' => 'publish', 'channel' => 'some channel', 'data' => 'some data' }
|
113
|
-
|
113
|
+
subject.expects(:publish_event).with(data['channel'], data['data'].to_json)
|
114
114
|
subject.delegate_action(data)
|
115
115
|
end
|
116
116
|
|
@@ -196,7 +196,7 @@ describe CelluloidPubsub::Reactor do
|
|
196
196
|
describe '#add_subscriber_to_channel' do
|
197
197
|
let(:channel) { 'some channel' }
|
198
198
|
let(:message) { { a: 'b' } }
|
199
|
-
let(:subscribers) {mock}
|
199
|
+
let(:subscribers) { mock }
|
200
200
|
|
201
201
|
it 'adds subscribed' do
|
202
202
|
CelluloidPubsub::Registry.channels.stubs(:include?).with(channel).returns(false)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: celluloid_pubsub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- bogdanRada
|
@@ -419,8 +419,7 @@ files:
|
|
419
419
|
- lib/celluloid_pubsub/client.rb
|
420
420
|
- lib/celluloid_pubsub/helper.rb
|
421
421
|
- lib/celluloid_pubsub/reactor.rb
|
422
|
-
- lib/celluloid_pubsub/
|
423
|
-
- lib/celluloid_pubsub/redis/redis_reactor.rb
|
422
|
+
- lib/celluloid_pubsub/redis_reactor.rb
|
424
423
|
- lib/celluloid_pubsub/registry.rb
|
425
424
|
- lib/celluloid_pubsub/version.rb
|
426
425
|
- lib/celluloid_pubsub/web_server.rb
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require_relative '../helper'
|
2
|
-
module CelluloidPubsub
|
3
|
-
# class that handles redis connection
|
4
|
-
class Redis
|
5
|
-
class << self
|
6
|
-
include Celluloid::Logger
|
7
|
-
include CelluloidPubsub::Helper
|
8
|
-
|
9
|
-
@connected ||= false
|
10
|
-
attr_accessor :connected, :connection
|
11
|
-
|
12
|
-
alias_method :connected?, :connected
|
13
|
-
|
14
|
-
def connect(&block)
|
15
|
-
require 'eventmachine'
|
16
|
-
require 'em-hiredis'
|
17
|
-
run_the_eventmachine(&block)
|
18
|
-
setup_em_exception_handler
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def run_the_eventmachine(&block)
|
24
|
-
EM.run do
|
25
|
-
@connection ||= EM::Hiredis.connect
|
26
|
-
@connected = true
|
27
|
-
block.call @connection
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def setup_em_exception_handler
|
32
|
-
EM.error_handler do |error|
|
33
|
-
debug error unless filtered_error?(error)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
require_relative '../reactor'
|
2
|
-
module CelluloidPubsub
|
3
|
-
# reactor used for redis pubsub
|
4
|
-
class RedisReactor < CelluloidPubsub::Reactor
|
5
|
-
include Celluloid
|
6
|
-
include Celluloid::IO
|
7
|
-
include Celluloid::Logger
|
8
|
-
|
9
|
-
def unsubscribe(channel)
|
10
|
-
super
|
11
|
-
async.redis_action('unsubscribe', channel)
|
12
|
-
end
|
13
|
-
|
14
|
-
def add_subscriber_to_channel(channel, message)
|
15
|
-
super
|
16
|
-
async.redis_action('subscribe', channel, message)
|
17
|
-
end
|
18
|
-
|
19
|
-
def unsubscribe_from_channel(channel)
|
20
|
-
super
|
21
|
-
async.redis_action('unsubscribe', channel)
|
22
|
-
end
|
23
|
-
|
24
|
-
def unsubscribe_all
|
25
|
-
CelluloidPubsub::Registry.channels.map do |channel|
|
26
|
-
async.redis_action('unsubscribe', channel)
|
27
|
-
end
|
28
|
-
info 'clearing connections'
|
29
|
-
shutdown
|
30
|
-
end
|
31
|
-
|
32
|
-
def shutdown
|
33
|
-
@channels.each do |channel|
|
34
|
-
redis_action('unsubscribe', channel)
|
35
|
-
end if @channels.present?
|
36
|
-
super
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def fetch_pubsub
|
42
|
-
CelluloidPubsub::Redis.connect do |connection|
|
43
|
-
yield connection.pubsub
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def action_subscribe?(action)
|
48
|
-
action == 'subscribe'
|
49
|
-
end
|
50
|
-
|
51
|
-
def action_success(action, channel, message)
|
52
|
-
action_subscribe?(action) ? message.merge('client_action' => 'successful_subscription', 'channel' => channel) : nil
|
53
|
-
end
|
54
|
-
|
55
|
-
def redis_action(action, channel = nil, message = {})
|
56
|
-
fetch_pubsub do |pubsub|
|
57
|
-
callback = prepare_redis_action(action)
|
58
|
-
success_message = action_success(action, channel, message)
|
59
|
-
subscription = pubsub.send(action, channel, callback)
|
60
|
-
handle_redis_action(subscription, action, success_message)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def prepare_redis_action(action)
|
65
|
-
log_unsubscriptions(pubsub)
|
66
|
-
proc do |subscribed_message|
|
67
|
-
action_subscribe?(action) ? (@websocket << subscribed_message) : log_debug(message)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def log_unsubscriptions(pubsub)
|
72
|
-
pubsub.on(:unsubscribe) do |subscribed_channel, remaining_subscriptions|
|
73
|
-
log_debug [:unsubscribe_happened, subscribed_channel, remaining_subscriptions].inspect
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def handle_redis_action(subscription, action, sucess_message = nil)
|
78
|
-
register_redis_callback(subscription, sucess_message)
|
79
|
-
register_redis_error_callback(subscription, action)
|
80
|
-
end
|
81
|
-
|
82
|
-
def register_redis_callback(subscription, sucess_message = nil)
|
83
|
-
subscription.callback do |subscriptions_ids|
|
84
|
-
if sucess_message.present?
|
85
|
-
@websocket << sucess_message.merge('subscriptions' => subscriptions_ids).to_json
|
86
|
-
else
|
87
|
-
log_debug "#{action} success #{success_response.inspect}"
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def register_redis_error_callback(subscription, action)
|
93
|
-
subscription.errback { |reply| log_debug "#{action} error #{reply.inspect}" }
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|