celluloid_pubsub 0.0.5 → 0.0.6

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.
@@ -0,0 +1,112 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.8.7.6
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ hasFrames = window.top.frames.main ? true : false;
19
+ relpath = '';
20
+ framesUrl = "frames.html#!top-level-namespace.html";
21
+ </script>
22
+
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
25
+
26
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
27
+
28
+
29
+ </head>
30
+ <body>
31
+ <div id="header">
32
+ <div id="menu">
33
+
34
+ <a href="_index.html">Index</a> &raquo;
35
+
36
+
37
+ <span class="title">Top Level Namespace</span>
38
+
39
+
40
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
41
+ </div>
42
+
43
+ <div id="search">
44
+
45
+ <a class="full_list_link" id="class_list_link"
46
+ href="class_list.html">
47
+ Class List
48
+ </a>
49
+
50
+ <a class="full_list_link" id="method_list_link"
51
+ href="method_list.html">
52
+ Method List
53
+ </a>
54
+
55
+ <a class="full_list_link" id="file_list_link"
56
+ href="file_list.html">
57
+ File List
58
+ </a>
59
+
60
+ </div>
61
+ <div class="clear"></div>
62
+ </div>
63
+
64
+ <iframe id="search_frame"></iframe>
65
+
66
+ <div id="content"><h1>Top Level Namespace
67
+
68
+
69
+
70
+ </h1>
71
+
72
+ <dl class="box">
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+ </dl>
82
+ <div class="clear"></div>
83
+
84
+ <h2>Defined Under Namespace</h2>
85
+ <p class="children">
86
+
87
+
88
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="CelluloidPubsub.html" title="CelluloidPubsub (module)">CelluloidPubsub</a></span>
89
+
90
+
91
+
92
+
93
+ </p>
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ </div>
104
+
105
+ <div id="footer">
106
+ Generated on Fri Mar 13 12:25:42 2015 by
107
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
+ 0.8.7.6 (ruby-2.0.0).
109
+ </div>
110
+
111
+ </body>
112
+ </html>
@@ -1,6 +1,7 @@
1
1
  require 'bundler/setup'
2
2
  require 'celluloid_pubsub'
3
3
 
4
+ # actor that subscribes to a channel
4
5
  class Subscriber
5
6
  include Celluloid
6
7
  include Celluloid::Logger
@@ -18,9 +19,10 @@ class Subscriber
18
19
  def on_close(code, reason)
19
20
  puts "websocket connection closed: #{code.inspect}, #{reason.inspect}"
20
21
  terminate
21
- end
22
22
  end
23
+ end
23
24
 
25
+ # actor that publishes a message in a channel
24
26
  class Publisher
25
27
  include Celluloid
26
28
  include Celluloid::Logger
@@ -1,10 +1,49 @@
1
1
  module CelluloidPubsub
2
+ # class used to make a new connection in order to subscribe or publish to a channel
2
3
  class Client
4
+ # worker that subscribes to a channel or publishes to a channel
5
+ # if it used to subscribe to a channel the worker will dispatch the messages to the actor that made the
6
+ # connection in the first place.
7
+ #
8
+ # @!attribute actor
9
+ # @return [Celluloid::Actor] actor to which callbacks will be delegated to
10
+ #
11
+ # @!attribute connect_blk
12
+ # @return [Proc] Block that will execute after the connection is opened
13
+ #
14
+ # @!attribute client
15
+ # @return [Celluloid::WebSocket::Client] A websocket client that is used to chat witht the webserver
16
+ #
17
+ # @!attribute options
18
+ # @return [Hash] the options that can be used to connect to webser and send additional data
19
+ #
20
+ # @!attribute hostname
21
+ # @return [String] The hostname on which the webserver runs on
22
+ #
23
+ # @!attribute port
24
+ # @return [String] The port on which the webserver runs on
25
+ #
26
+ # @!attribute path
27
+ # @return [String] The hostname on which the webserver runs on
3
28
  class PubSubWorker
4
29
  include Celluloid
5
30
  include Celluloid::Logger
6
31
  attr_accessor :actor, :connect_blk, :client, :options, :hostname, :port, :path
7
32
 
33
+ # receives a list of options that are used to connect to the webserver and an actor to which the callbacks are delegated to
34
+ # when receiving messages from a channel
35
+ #
36
+ # @param [Hash] options the options that can be used to connect to webser and send additional data
37
+ # @option options [String] :actor The actor that made the connection
38
+ # @option options [String]:hostname The hostname on which the webserver runs on
39
+ # @option options [String] :port The port on which the webserver runs on
40
+ # @option options [String] :path The request path that the webserver accepts
41
+ #
42
+ # @param [Proc] connect_blk Block that will execute after the connection is opened
43
+ #
44
+ # @return [void]
45
+ #
46
+ # @api public
8
47
  def initialize(options, &connect_blk)
9
48
  parse_options(options)
10
49
  raise "#{self}: Please provide an actor in the options list!!!" if @actor.blank?
@@ -12,6 +51,17 @@ module CelluloidPubsub
12
51
  @client = Celluloid::WebSocket::Client.new("ws://#{@hostname}:#{@port}#{@path}", Actor.current)
13
52
  end
14
53
 
54
+ # check the options list for values and sets default values if not found
55
+ #
56
+ # @param [Hash] options the options that can be used to connect to webser and send additional data
57
+ # @option options [String] :actor The actor that made the connection
58
+ # @option options [String]:hostname The hostname on which the webserver runs on
59
+ # @option options [String] :port The port on which the webserver runs on
60
+ # @option options [String] :path The request path that the webserver accepts
61
+ #
62
+ # @return [void]
63
+ #
64
+ # @api public
15
65
  def parse_options(options)
16
66
  raise 'Options is not a hash' unless options.is_a?(Hash)
17
67
  @options = options.stringify_keys!
@@ -21,27 +71,62 @@ module CelluloidPubsub
21
71
  @path = @options.fetch('path', CelluloidPubsub::WebServer::PATH)
22
72
  end
23
73
 
74
+ # checks if debug is enabled
75
+ #
76
+ #
77
+ # @return [boolean]
78
+ #
79
+ # @api public
24
80
  def debug_enabled?
25
81
  CelluloidPubsub::WebServer.debug_enabled?
26
82
  end
27
83
 
84
+ # subscribes to a channel . need to be used inside the connect block passed to the actor
85
+ #
86
+ # @param [string] channel
87
+ #
88
+ # @return [void]
89
+ #
90
+ # @api public
28
91
  def subscribe(channel)
29
92
  subscription_data = { 'client_action' => 'subscribe', 'channel' => channel }
30
93
  debug("#{self.class} tries to subscribe #{subscription_data}") if debug_enabled?
31
94
  async.chat(subscription_data)
32
95
  end
33
96
 
97
+ # publishes to a channel some data (can be anything)
98
+ #
99
+ # @param [string] channel
100
+ # @param [#to_s] data
101
+ #
102
+ # @return [void]
103
+ #
104
+ # @api public
34
105
  def publish(channel, data)
35
106
  publishing_data = { 'client_action' => 'publish', 'channel' => channel, 'data' => data }
36
107
  debug(" #{self.class} publishl to #{channel} message: #{publishing_data}") if debug_enabled?
37
108
  async.chat(publishing_data)
38
109
  end
39
110
 
111
+ # callback executes after connection is opened and delegates action to actor
112
+ #
113
+ # @return [void]
114
+ #
115
+ # @api public
40
116
  def on_open
41
117
  debug("#{self.class} websocket connection opened") if debug_enabled?
42
118
  @connect_blk.call Actor.current
43
119
  end
44
120
 
121
+ # callback executes when actor receives a message from a subscribed channel
122
+ # and parses the message using JSON.parse and dispatches the parsed
123
+ # message to the original actor that made the connection
124
+ #
125
+ # @param [JSON] data
126
+ #
127
+ # @return [void]
128
+ #
129
+ # @api public
45
130
  def on_message(data)
46
131
  debug("#{self.class} received plain #{data}") if debug_enabled?
47
132
  message = JSON.parse(data)
@@ -49,6 +134,15 @@ module CelluloidPubsub
49
134
  @actor.async.on_message(message)
50
135
  end
51
136
 
137
+ # callback executes when connection closes
138
+ #
139
+ # @param [String] code
140
+ #
141
+ # @param [String] reason
142
+ #
143
+ # @return [void]
144
+ #
145
+ # @api public
52
146
  def on_close(code, reason)
53
147
  @client.terminate
54
148
  terminate
@@ -58,6 +152,15 @@ module CelluloidPubsub
58
152
 
59
153
  private
60
154
 
155
+ # method used to send messages to the webserver
156
+ # checks too see if the message is a hash and if it is it will transform it to JSON and send it to the webser
157
+ # otherwise will construct a JSON object that will have the key action with the value 'message" and the key message witth the parameter's value
158
+ #
159
+ # @param [Hash] message
160
+ #
161
+ # @return [void]
162
+ #
163
+ # @api private
61
164
  def chat(message)
62
165
  final_message = nil
63
166
  if message.is_a?(Hash)
@@ -72,6 +175,20 @@ module CelluloidPubsub
72
175
  end
73
176
  end
74
177
 
178
+ # method used to make a new connection to the webserver
179
+ # after the connection is opened it will execute the block that is passed as argument
180
+ #
181
+ # @param [Hash] options the options that can be used to connect to webser and send additional data
182
+ # @option options [String] :actor The actor that made the connection
183
+ # @option options [String]:hostname The hostname on which the webserver runs on
184
+ # @option options [String] :port The port on which the webserver runs on
185
+ # @option options [String] :path The request path that the webserver accepts
186
+ #
187
+ # @param [Proc] connect_blk Block that will execute after the connection is opened
188
+ #
189
+ # @return [CelluloidPubsub::Client::PubSubWorker]
190
+ #
191
+ # @api public
75
192
  def self.connect(options = {}, &connect_blk)
76
193
  CelluloidPubsub::Client::PubSubWorker.new(options, &connect_blk)
77
194
  end
@@ -1,26 +1,62 @@
1
1
  require_relative './registry'
2
2
  module CelluloidPubsub
3
+ # The reactor handles new connections. Based on what the client sends it either subscribes to a channel
4
+ # or will publish to a channel or just dispatch to the server if command is neither subscribe, publish or unsubscribe
5
+ # @!attribute websocket
6
+ # @return [Reel::WebSocket] websocket connection
7
+ #
8
+ # @!attribute server
9
+ # @return [CelluloidPubsub::Webserver] the server actor to which the reactor is connected to
10
+ #
11
+ # @!attribute mutex
12
+ # @return [Mutex] mutex used to lock when subscribing to a channel
13
+ #
14
+ # @!attribute channels
15
+ # @return [Array] array of channels to which the current reactor has subscribed to
3
16
  class Reactor
4
17
  include Celluloid
5
18
  include Celluloid::IO
6
19
  include Celluloid::Logger
7
20
 
8
- attr_accessor :websocket, :server, :mutex
21
+ attr_accessor :websocket, :server, :mutex, :channels
9
22
 
23
+ # rececives a new socket connection from the server
24
+ # and initiates a new mutex that can be used when subsribing to a channel
25
+ #
26
+ # @param [Reel::WebSocket] websocket
27
+ #
28
+ # @return [void]
29
+ #
30
+ # @api public
10
31
  def work(websocket, server)
11
32
  @server = server
12
33
  @mutex = Mutex.new
34
+ @channels = []
13
35
  @websocket = websocket
14
36
  info "Streaming changes for #{websocket.url}" if @server.debug_enabled?
15
37
  async.run
16
38
  end
17
39
 
40
+ # reads from the socket the message
41
+ # and dispatches it to the handle_websocket_message method
42
+ # @see #handle_websocket_message
43
+ #
44
+ # @return [void]
45
+ #
46
+ # @api public
18
47
  def run
19
48
  while message = @websocket.read
20
49
  handle_websocket_message(message)
21
50
  end
22
51
  end
23
52
 
53
+ # method used to parse a JSON object into a Hash object
54
+ #
55
+ # @param [JSON] message
56
+ #
57
+ # @return [Hash]
58
+ #
59
+ # @api public
24
60
  def parse_json_data(message)
25
61
  debug "Reactor read message #{message}" if @server.debug_enabled?
26
62
  json_data = nil
@@ -34,11 +70,37 @@ module CelluloidPubsub
34
70
  json_data
35
71
  end
36
72
 
73
+ # method that handles the message received from the websocket connection
74
+ # first will try to parse the message {#parse_json_data} and then it will dispatch
75
+ # it to another method that will decide depending the message what action
76
+ # should the reactor take {#handle_parsed_websocket_message}
77
+ #
78
+ # @see #parse_json_data
79
+ # @see #handle_parsed_websocket_message
80
+ #
81
+ # @param [JSON] message
82
+ #
83
+ # @return [void]
84
+ #
85
+ # @api public
37
86
  def handle_websocket_message(message)
38
87
  json_data = parse_json_data(message)
39
88
  handle_parsed_websocket_message(json_data)
40
89
  end
41
90
 
91
+ # method that checks if the data is a Hash
92
+ #
93
+ # if the data is a hash then will stringify the keys and will call the method {#delegate_action}
94
+ # that will handle the message, otherwise will call the method {#handle_unknown_action}
95
+ #
96
+ # @see #delegate_action
97
+ # @see #handle_unknown_action
98
+ #
99
+ # @param [Hash] json_data
100
+ #
101
+ # @return [void]
102
+ #
103
+ # @api public
42
104
  def handle_parsed_websocket_message(json_data)
43
105
  if json_data.is_a?(Hash)
44
106
  json_data = json_data.stringify_keys
@@ -49,6 +111,27 @@ module CelluloidPubsub
49
111
  end
50
112
  end
51
113
 
114
+ # method that checks if the data is a Hash
115
+ #
116
+ # if the data is a hash then will stringify the keys and will call the method {#delegate_action}
117
+ # that will handle the message, otherwise will call the method {#handle_unknown_action}
118
+ #
119
+ # @see #delegate_action
120
+ # @see #handle_unknown_action
121
+ #
122
+ # @param [Hash] json_data
123
+ # @option json_data [String] :client_action The action based on which the reactor will decide what action should make
124
+ #
125
+ # Possible values are:
126
+ # unsubscribe_client
127
+ # unsubscribe
128
+ # subscribe
129
+ # publish
130
+ #
131
+ #
132
+ # @return [void]
133
+ #
134
+ # @api public
52
135
  def delegate_action(json_data)
53
136
  case json_data['client_action']
54
137
  when 'unsubscribe_all'
@@ -64,44 +147,92 @@ module CelluloidPubsub
64
147
  end
65
148
  end
66
149
 
150
+ # the method will delegate the message to the server in an asyncronous way by sending the current actor and the message
151
+ # @see {CelluloidPubsub::WebServer#handle_dispatched_message}
152
+ #
153
+ # @param [Hash] json_data
154
+ #
155
+ # @return [void]
156
+ #
157
+ # @api public
67
158
  def handle_unknown_action(json_data)
68
159
  debug "Trying to dispatch to server #{json_data}" if @server.debug_enabled?
69
160
  @server.async.handle_dispatched_message(Actor.current, json_data)
70
161
  end
71
162
 
163
+ # the method will unsubscribe a client by closing the websocket connection if has unscribed from all channels
164
+ # and deleting the reactor from the channel list on the server
165
+ #
166
+ # @param [String] channel
167
+ #
168
+ # @return [void]
169
+ #
170
+ # @api public
72
171
  def unsubscribe_client(channel)
73
172
  return unless channel.present?
74
- @websocket.close
75
- @server.unsubscribe_client(Actor.current, channel)
173
+ @channels.delete(channel)
174
+ @websocket.close if @channels.blank?
175
+ @server.subscribers[channel].delete_if do |hash|
176
+ hash[:reactor] == Actor.current
177
+ end
76
178
  end
77
179
 
180
+ # the method will terminate the current actor
181
+ #
182
+ #
183
+ # @return [void]
184
+ #
185
+ # @api public
78
186
  def shutdown
79
187
  terminate
80
188
  end
81
189
 
190
+ # this method will add the current actor to the list of the subscribers {#add_subscriber_to_channel}
191
+ # and will write to the socket a message for succesful subscription
192
+ #
193
+ # @see #add_subscriber_to_channel
194
+ #
195
+ # @param [String] channel
196
+ # @param [Object] message
197
+ #
198
+ # @return [void]
199
+ #
200
+ # @api public
82
201
  def start_subscriber(channel, message)
83
202
  return unless channel.present?
84
- @mutex.lock
85
203
  begin
86
204
  add_subscriber_to_channel(channel, message)
87
205
  debug "Subscribed to #{channel} with #{message}" if @server.debug_enabled?
88
206
  @websocket << message.merge('client_action' => 'successful_subscription', 'channel' => channel).to_json
89
207
  rescue => e
90
208
  raise [e, e.respond_to?(:backtrace) ? e.backtrace : '', channel, message].inspect
91
- ensure
92
- @mutex.unlock
93
209
  end
94
210
  end
95
211
 
212
+ # adds the curent actor the list of the subscribers for a particular channel
213
+ # and registers the new channel
214
+ #
215
+ # @param [String] channel
216
+ # @param [Object] message
217
+ #
218
+ # @return [void]
219
+ #
220
+ # @api public
96
221
  def add_subscriber_to_channel(channel, message)
222
+ @channels << channel
97
223
  CelluloidPubsub::Registry.channels << channel unless CelluloidPubsub::Registry.channels.include?(channel)
98
224
  @server.subscribers[channel] ||= []
99
225
  @server.subscribers[channel] << { reactor: Actor.current, message: message }
100
226
  end
101
227
 
228
+ # unsubscribes all actors from all channels and terminates the curent actor
229
+ #
230
+ # @return [void]
231
+ #
232
+ # @api public
102
233
  def unsubscribe_all
103
234
  CelluloidPubsub::Registry.channels.map do |channel|
104
- @subscribers[channel].each do |hash|
235
+ @server.subscribers[channel].each do |hash|
105
236
  hash[:reactor].websocket.close
106
237
  end
107
238
  @server.subscribers[channel] = []
@@ -1,7 +1,10 @@
1
1
  module CelluloidPubsub
2
+ # class used to register new channels and save them in memory
2
3
  class Registry
3
4
  include ActiveSupport::Configurable
4
5
  class << self
6
+ # @!attribute channels
7
+ # @return [Array] array of channels to which actors have subscribed to
5
8
  attr_accessor :channels
6
9
  end
7
10
  @channels = []
@@ -1,14 +1,27 @@
1
- module CelluloidPubsub # Returns the version of the currently loaded Rails as a <tt>Gem::Version</tt>
1
+ # Returns the version of the gem as a <tt>Gem::Version</tt>
2
+ module CelluloidPubsub
3
+ # it prints the gem version as a string
4
+ #
5
+ # @return [String]
6
+ #
7
+ # @api public
2
8
  def self.gem_version
3
9
  Gem::Version.new VERSION::STRING
4
10
  end
5
11
 
12
+ # module used to generate the version string
13
+ # provides a easy way of getting the major, minor and tiny
6
14
  module VERSION
15
+ # major release version
7
16
  MAJOR = 0
17
+ # minor release version
8
18
  MINOR = 0
9
- TINY = 5
19
+ # tiny release version
20
+ TINY = 6
21
+ # prelease version ( set this only if it is a prelease)
10
22
  PRE = nil
11
23
 
24
+ # generates the version string
12
25
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
13
26
  end
14
27
  end