celluloid_pubsub 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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