celluloid_pubsub 0.3.3 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0d8a66626deefeec22f4ab76d4c1193522c0fcd6
4
- data.tar.gz: c34497b740e353741c4f8d4f4ef847769edfce54
3
+ metadata.gz: ef1607ef65dd326efbf983675c21f745babab415
4
+ data.tar.gz: fbcfed23005b8d5b22232396fb5106ef13b69877
5
5
  SHA512:
6
- metadata.gz: d66edfa90a6a35e17cc345bb68131b8f18636c04b7ce4f0a56bffa7a009f9cd73bed2596c08fa3a06dbd2097ad2638fb6fca16e96363d1c683fdaa16a9b5a4ce
7
- data.tar.gz: a225985eb7278dda9f4c08b30bf19dd38e2d13b06488c8aa8207fcae3b29c1b49cc293fa90f9fcdda6a1f9ad28b075850ee4e9201c85afabe2376ef3a1eb9570
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 hostname
20
- # @return [String] The hostname on which the webserver runs on
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
- attr_reader :actor, :connection, :options, :hostname, :port, :path, :channel
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 [void]
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 are used to configure the webserver
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 can be used to connect to webser and send additional data
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 [void]
80
+ # @return [Hash]
52
81
  #
53
- # @api public
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
- # unsubscribe_client
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
- @server.publish_event(channel, json_data['data'].to_json)
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
@@ -15,9 +15,9 @@ module CelluloidPubsub
15
15
  # major release version
16
16
  MAJOR = 0
17
17
  # minor release version
18
- MINOR = 3
18
+ MINOR = 4
19
19
  # tiny release version
20
- TINY = 3
20
+ TINY = 1
21
21
  # prelease version ( set this only if it is a prelease)
22
22
  PRE = nil
23
23
 
@@ -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, :backlog, :hostname, :port, :path, :spy, :use_redis, :debug_enabled
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
- # :nocov:
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 [void]
146
+ # @return [Boolean] returns true if redis is enabled otherwise false
123
147
  #
124
148
  # @api public
125
- def publish_event(current_topic, message)
126
- if redis_enabled?
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
- server.expects(:publish_event).with(data['channel'], data['data'].to_json)
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.3.3
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/redis/redis.rb
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