kosmonaut 0.3.0 → 0.3.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.
data/NEWS CHANGED
@@ -1,6 +1,30 @@
1
1
  Releases
2
2
  ========
3
3
 
4
+ v0.3.1
5
+ ------
6
+ * Documentation.
7
+ * Listener doesn't try to reconnect when unauthenticated.
8
+
9
+ v0.3.0
10
+ ------
11
+ * Kosmonaut::Client#request_single_access token gets 2 arguments now - uid and
12
+ permission - according to new spec and implementation of WebRocket.
13
+
14
+ v0.2.4
15
+ ------
16
+ * Handled broken pipe error.
17
+ * Timeouts switched to raw socket options.
18
+
19
+ v0.2.3
20
+ ------
21
+ * Worker's listener tweaks and optimization.
22
+
23
+ v0.2.2
24
+ ------
25
+ * Optimized timeouts.
26
+ * Fixed bug in opening new channel.
27
+
4
28
  v0.2.1
5
29
  ------
6
30
  * Fixed behaviour of the worker's listener.
@@ -8,4 +32,4 @@ v0.2.1
8
32
  v0.2.0
9
33
  ------
10
34
  * Pure ruby implementation of the client and worker using majordomo pattern.
11
- * Tests for the API
35
+ * Tests for the API
data/README CHANGED
@@ -85,7 +85,7 @@ or email one of the maintainers.
85
85
  Sponsors
86
86
  --------
87
87
  All the work on the project is sponsored and supported by Cubox - an
88
- awesome dev shop from Uruguay <http://cuboxsa.com>.
88
+ awesome dev shop from Uruguay <http://cuboxlabs.com>.
89
89
 
90
90
  Copyright
91
91
  ---------
data/Rakefile CHANGED
@@ -1,14 +1,12 @@
1
1
  # -*- ruby -*-
2
2
 
3
- =begin
4
- require 'rdoc/task'
5
- Rake::RDocTask.new do |rdoc|
6
- rdoc.rdoc_dir = 'rdoc'
7
- rdoc.title = "Kosmonaut - The WebRocket backend client"
8
- rdoc.rdoc_files.include('README*')
9
- rdoc.rdoc_files.include('lib/**/*.rb')
10
- end
11
- =end
3
+ #require 'rdoc/task'
4
+ #Rake::RDocTask.new do |rdoc|
5
+ # rdoc.rdoc_dir = 'rdoc'
6
+ # rdoc.title = "Kosmonaut - The WebRocket backend client"
7
+ # rdoc.rdoc_files.include('README*')
8
+ # rdoc.rdoc_files.include('lib/**/*.rb')
9
+ #end
12
10
 
13
11
  require 'rake/testtask'
14
12
  Rake::TestTask.new do |t|
@@ -1,3 +1,13 @@
1
+ # Copyright (C) 2012 Krzysztof Kowalik <chris@nu7hat.ch> and folks at Cubox
2
+ #
3
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9
+ # SOFTWARE.
10
+
1
11
  require 'kosmonaut/errors'
2
12
  require 'kosmonaut/socket'
3
13
  require 'kosmonaut/worker'
@@ -1,25 +1,45 @@
1
+ # Copyright (C) 2012 Krzysztof Kowalik <chris@nu7hat.ch> and folks at Cubox
2
+ #
3
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9
+ # SOFTWARE.
10
+
1
11
  require 'json'
2
12
  require 'thread'
3
13
 
4
14
  module Kosmonaut
15
+ # Public: Client is an implementation REQ-REP type socket which handles
16
+ # communication between backend application and WebRocket backend endpoint.
17
+ #
18
+ # Client is used to synchronously request operations from the server.
19
+ # Synchronous operations are used to provide consistency for the backed
20
+ # generated events.
21
+ #
22
+ # Examples
23
+ #
24
+ # c = Kosmonaut::Client.new("wr://token@127.0.0.1:8081/vhost")
25
+ # c.open_channel("comments")
26
+ #
27
+ # @comment = User.new(params[:comment])
28
+ #
29
+ # if @comment.save
30
+ # c.broadcast("comments", "comment_added", @comment.to_json)
31
+ # # ...
32
+ # end
33
+ #
5
34
  class Client < Socket
6
35
  # Maximum number of seconds to wait for the request to being processed.
7
36
  REQUEST_TIMEOUT = 5.0
8
37
 
9
38
  # Public: The Client constructor. Pre-configures the client instance. See
10
- # also the Kosmonaut::Socket#initialize for the details.
39
+ # also the Kosmonaut::Socket#initialize to get more information.
11
40
  #
12
41
  # url - The WebRocket backend endpoint URL to connect to.
13
42
  #
14
- # The endpoint's URL must have the following format:
15
- #
16
- # [scheme]://[secret]@[host]:[port]/[vhost]
17
- #
18
- # Examples
19
- #
20
- # c = Kosmonaut::Client.new("wr://f343...fa4a@myhost.com:8080/hello")
21
- # c.broadcast("room", "status", {"message" => "is going to the beach!"})
22
- #
23
43
  def initialize(url)
24
44
  super(url)
25
45
  @mtx = Mutex.new
@@ -38,32 +58,81 @@ module Kosmonaut
38
58
  # c.broadcast("room". "message", {"content" => "Hello World!"})
39
59
  # c.broadcast("room". "status", {"message" => "is saying hello!"})
40
60
  #
61
+ # Returns 0 if succeed.
62
+ # Raises one of the Kosmonaut::Error inherited exceptions.
41
63
  def broadcast(channel, event, data)
42
64
  payload = ["BC", channel, event, data.to_json]
43
65
  perform_request(payload)
44
66
  end
45
-
67
+
68
+ # Public: Opens specified channel. If channel already exists, then ok
69
+ # response will be received anyway. If channel name is starts with the
70
+ # `presence-` or `private-` prefix, then appropriate type of the channel
71
+ # will be created.
72
+ #
73
+ # name - A name of the channel to be created.
74
+ #
75
+ # Examples
76
+ #
77
+ # c.open_channel("room")
78
+ # c.open_channel("presence-room")
79
+ # c.open_channel("private-room")
80
+ #
81
+ # Returns 0 if succeed.
82
+ # Raises one of the Kosmonaut::Error inherited exceptions.
46
83
  def open_channel(name)
47
84
  payload = ["OC", name]
48
85
  perform_request(payload)
49
86
  end
50
87
 
88
+ # Public: Closes specified channel. If channel doesn't exist then an
89
+ # error will be thrown.
90
+ #
91
+ # name - A name of the channel to be deleted.
92
+ #
93
+ # Examples
94
+ #
95
+ # c.close_channel("hello")
96
+ # c.close_channel("presence-room")
97
+ #
98
+ # Returns 0 if succeed.
99
+ # Raises one of the Kosmonaut::Error inherited exceptions.
51
100
  def close_channel(name)
52
101
  payload = ["CC", name]
53
102
  perform_request(payload)
54
103
  end
55
104
 
105
+ # Public: Sends a request to generate a single access token for given
106
+ # user with specified permissions.
107
+ #
108
+ # uid - An user defined unique ID.
109
+ # permission - A permissions regexp to match against the channels.
110
+ #
111
+ # Examples
112
+ #
113
+ # @current_user = User.find(params[:id])
114
+ # c.request_single_access_token(@current_user.username, ".*")
115
+ #
116
+ #
117
+ # Returns generated access token string if succeed.
118
+ # Raises one of the Kosmonaut::Error inherited exceptions.
56
119
  def request_single_access_token(uid, permission)
57
120
  payload = ["AT", uid, permission]
58
121
  perform_request(payload)
59
122
  end
60
123
 
124
+ private
125
+
126
+ # Internal: Returns abbreviation of the socket type.
61
127
  def socket_type
62
128
  "req"
63
129
  end
64
130
 
65
- private
66
-
131
+ # Internal: Performs request with specified payload and waits for the
132
+ # response with it's result.
133
+ #
134
+ # Returns response result if succeed.
135
+ # Raises one of the Kosmonaut::Error inherited exceptions.
67
136
  def perform_request(payload)
68
137
  @mtx.synchronize {
69
138
  response = []
@@ -77,6 +146,18 @@ module Kosmonaut
77
146
  }
78
147
  end
79
148
 
149
+ # Internal: Parses given response and discovers it's result according
150
+ # to the WebRocket Backend Protocol specification.
151
+ #
152
+ # Response format
153
+ #
154
+ # 0x01 | command \n |
155
+ # 0x02 | payload... \n | *
156
+ # 0x.. | ... \n | *
157
+ # | \r\n\r\n |
158
+ #
159
+ # Returns response result if succeed.
160
+ # Raises one of the Kosmonaut::Error inherited exceptions.
80
161
  def parse_response(response)
81
162
  cmd = response[0].to_s
82
163
  Kosmonaut.log("Client/RES : #{response.join("\n").inspect}")
@@ -1,3 +1,13 @@
1
+ # Copyright (C) 2012 Krzysztof Kowalik <chris@nu7hat.ch> and folks at Cubox
2
+ #
3
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9
+ # SOFTWARE.
10
+
1
11
  module Kosmonaut
2
12
  class Error < StandardError
3
13
  end
@@ -1,33 +1,77 @@
1
+ # Copyright (C) 2012 Krzysztof Kowalik <chris@nu7hat.ch> and folks at Cubox
2
+ #
3
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9
+ # SOFTWARE.
10
+
1
11
  require 'uri'
2
12
  require 'socket'
3
13
  require 'securerandom'
4
14
 
5
15
  module Kosmonaut
16
+ # Internal: Socket is a base class defining tools and helpers used by
17
+ # Client and Worker implementations.
6
18
  class Socket
19
+ # Public: A parsed URL of the WebRocket backend endpoint.
7
20
  attr_reader :uri
8
21
 
22
+ # Internal: The Socket constructor.
23
+ #
24
+ # The endpoint's URL must have the following format:
25
+ #
26
+ # [scheme]://[secret]@[host]:[port]/[vhost]
27
+ #
9
28
  def initialize(url)
10
29
  @uri = URI.parse(url)
11
30
  @addr = ::Socket.getaddrinfo(@uri.host, nil)
12
31
  end
13
32
 
33
+ protected
34
+
35
+ # Internal: Connect creates new connection with the backend endpoint.
36
+ #
37
+ # timeout - A value of the maximum execution time (float).
38
+ #
39
+ # Returns configured and connected socket instance.
14
40
  def connect(timeout)
15
- secs = timeout.to_i
16
- usecs = ((timeout - secs) * 1_000_000).to_i
41
+ secs = timeout.to_i
42
+ usecs = ((timeout - secs) * 1_000_000).to_i
17
43
  optval = [secs, usecs].pack("l_2")
18
44
 
45
+ # Raw timeouts are way way way faster than Timeout module or SystemTimer
46
+ # and doesn't need external dependencies.
19
47
  s = ::Socket.new(::Socket.const_get(@addr[0][0]), ::Socket::SOCK_STREAM, 0)
20
48
  s.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
21
49
  s.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_RCVTIMEO, optval)
22
50
  s.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDTIMEO, optval)
23
51
  s.connect(::Socket.pack_sockaddr_in(@uri.port, @addr[0][3]))
24
52
 
25
- generate_identity
53
+ generate_identity!
26
54
  return s
27
55
  end
28
56
 
29
- protected
30
-
57
+ # Internal: Pack converts given payload into single packet in format
58
+ # defined by WebRocket Backend Protocol.
59
+ #
60
+ # Packet format
61
+ #
62
+ # 0x01 | identity \n | *
63
+ # 0x02 | \n | *
64
+ # 0x03 | command \n |
65
+ # 0x04 | payload... \n | *
66
+ # 0x.. | ... \n | *
67
+ # | \r\n\r\n |
68
+ #
69
+ # * - optional field
70
+ #
71
+ # payload - The data to be packed.
72
+ # with_identity - Whether identity should be prepend to the packet.
73
+ #
74
+ # Returns packed data.
31
75
  def pack(payload=[], with_identity=true)
32
76
  if with_identity
33
77
  payload.unshift("")
@@ -36,11 +80,16 @@ module Kosmonaut
36
80
  payload.join("\n") + "\n\r\n\r\n"
37
81
  end
38
82
 
39
- def recv(s)
83
+ # Internal: Reads one packet from given socket instance.
84
+ #
85
+ # sock - The socket to receive from.
86
+ #
87
+ # Returns received data (frames) as an array of strings.
88
+ def recv(sock)
40
89
  data = []
41
90
  possible_eom = false # possible end of message
42
- while !s.eof?
43
- line = s.gets
91
+ while !sock.eof?
92
+ line = sock.gets
44
93
  if line == "\r\n"
45
94
  break if possible_eom
46
95
  possible_eom = true
@@ -54,13 +103,13 @@ module Kosmonaut
54
103
 
55
104
  private
56
105
 
57
- def generate_identity
58
- parts = []
59
- parts << socket_type
60
- parts << @uri.path
61
- parts << @uri.user # secret
62
- parts << SecureRandom.uuid
63
- @identity = parts.join(":")
106
+ # Internal: Generates unique identity for the socket connection.
107
+ # Identity is composed from the following parts:
108
+ #
109
+ # [socket-type]:[vhost]:[vhost-token]:[uuid]
110
+ #
111
+ def generate_identity!
112
+ @identity = [socket_type, @uri.path, @uri.user, SecureRandom.uuid].join(":")
64
113
  end
65
114
  end
66
115
  end
@@ -1,8 +1,18 @@
1
+ # Copyright (C) 2012 Krzysztof Kowalik <chris@nu7hat.ch> and folks at Cubox
2
+ #
3
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9
+ # SOFTWARE.
10
+
1
11
  module Kosmonaut
2
12
  module Version
3
13
  MAJOR = 0
4
14
  MINOR = 3
5
- PATCH = 0
15
+ PATCH = 1
6
16
 
7
17
  def self.to_s
8
18
  [MAJOR, MINOR, PATCH].join('.')
@@ -1,11 +1,62 @@
1
+ # Copyright (C) 2012 Krzysztof Kowalik <chris@nu7hat.ch> and folks at Cubox
2
+ #
3
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9
+ # SOFTWARE.
10
+
1
11
  require 'json'
2
12
  require 'thread'
3
13
 
4
14
  module Kosmonaut
15
+ # Public: Worker is an implementation of SUBSCRIBER socket which handles
16
+ # events incoming from the WebRocket backend endpoint.
17
+ #
18
+ # The communication here is asynchronous, and Worker can't send other
19
+ # messages than ready state, heartbeat, or quit notification.
20
+ #
21
+ # Worker shouldn't be called directly, it should be used as a base class
22
+ # for implementing your own workers.
23
+ #
24
+ # Examples
25
+ #
26
+ # class MyWorker < Kosmonaut::Worker
27
+ # def on_message(event, data)
28
+ # if event == "append_chat_history"
29
+ # @room = Room.find(data[:room_id])
30
+ # @room.messages.build(data[:message])
31
+ # @room.save!
32
+ # end
33
+ # end
34
+ #
35
+ # def on_error(err)
36
+ # puts "WEBROCKET ERROR: #{err.to_s}"
37
+ # end
38
+ #
39
+ # def on_exception(err)
40
+ # puts "INTERNAL ERROR: #{err.to_s}"
41
+ # end
42
+ # end
43
+ #
44
+ # w = MyWorker.new("wr://token@127.0.0.1:8081/vhost")
45
+ # w.listen
46
+ #
5
47
  class Worker < Socket
6
- RECONNECT_DELAY = 1000 # in milliseconds
7
- HEARTBEAT_INTERVAL = 500 # in milliseconds
48
+ # Number of milliseconds after which client should retry to reconnect
49
+ # to the bakcend endpoint.
50
+ RECONNECT_DELAY = 1000
8
51
 
52
+ # Number of milliseconds between next heartbeat message.
53
+ HEARTBEAT_INTERVAL = 500
54
+
55
+ # Public: The Worker constructor. Pre-configures the worker instance. See
56
+ # also the Kosmonaut::Socket#initialize to get more information.
57
+ #
58
+ # url - The WebRocket backend endpoint URL to connect to.
59
+ #
9
60
  def initialize(url)
10
61
  super(url)
11
62
  @mtx = Mutex.new
@@ -16,6 +67,10 @@ module Kosmonaut
16
67
  @heartbeat_at = 0
17
68
  end
18
69
 
70
+ # Public: Listen starts a listener's loop for the worker. Listener implements
71
+ # the Majordomo pattern to manage connection with the backend.
72
+ #
73
+ # Raises Kosmonaut::UnauthorizedError if worker's credentials are invalid.
19
74
  def listen
20
75
  @alive = true
21
76
  reconnect
@@ -31,7 +86,7 @@ module Kosmonaut
31
86
  end
32
87
 
33
88
  msg = recv(@sock)
34
- raise Errno::ECONNRESET if @sock.eof? || msg.empty?
89
+ raise Errno::ECONNRESET if msg.empty?
35
90
  Kosmonaut.log("Worker/RECV : #{msg.join("\n").inspect}")
36
91
  cmd = msg.shift
37
92
 
@@ -46,6 +101,8 @@ module Kosmonaut
46
101
  when "ER"
47
102
  error_handler(msg.size < 1 ? 597 : msg[0])
48
103
  end
104
+
105
+ raise Errno::ECONNRESET if @sock.eof?
49
106
  rescue Errno::EAGAIN, Errno::ECONNRESET, Errno::ECONNREFUSED, IOError => err
50
107
  Kosmonaut.log("Worker/RECONNECT: " + err.to_s)
51
108
  sleep(@reconnect_delay.to_f / 1000.0)
@@ -59,33 +116,49 @@ module Kosmonaut
59
116
  end
60
117
  end
61
118
 
119
+ # Public: Breaks the listener's loop and stops execution of
120
+ # the worker.
62
121
  def stop
63
122
  @mtx.synchronize { @alive = false }
64
123
  end
65
124
 
125
+ # Public: Returns whether this worker's event loop is running.
66
126
  def alive?
67
127
  @mtx.synchronize { @alive }
68
128
  end
69
129
 
130
+ private
131
+
132
+ # Internal: Returns abbreviation of the socket type.
70
133
  def socket_type
71
134
  "dlr"
72
135
  end
73
-
74
- private
75
-
76
- def send(s, payload, with_identity=false)
77
- return unless s
136
+
137
+ # Internal: Packs given payload and writes it to the specified socket.
138
+ #
139
+ # sock - The socket to write to.
140
+ # payload - The payload to be packed and sent.
141
+ # with_identity - Whether identity should be prepend to the packet.
142
+ #
143
+ def send(sock, payload, with_identity=false)
144
+ return unless sock
78
145
  packet = pack(payload, with_identity)
79
- s.write(packet)
146
+ sock.write(packet)
80
147
  Kosmonaut.log("Worker/SENT : #{packet.inspect}")
148
+ nil
81
149
  rescue Errno::EPIPE
82
150
  end
83
-
151
+
152
+ # Internal: Disconnects and cleans up the socket if connected.
84
153
  def disconnect
85
- @sock.close if @sock
154
+ @sock.close if @sock
155
+ @sock = nil
86
156
  rescue IOError
87
157
  end
88
-
158
+
159
+ # Internal: Sets up new connection with the backend endpoint and sends
160
+ # information that it's ready to work. Also initializes heartbeat
161
+ # scheduling.
89
162
  def reconnect
90
163
  @sock = connect(((@heartbeat_ivl * 2).to_f / 1000.0).to_i + 1)
91
164
  send(@sock, ["RD"], true)
@@ -93,6 +166,11 @@ module Kosmonaut
93
166
  rescue Errno::ECONNREFUSED
94
167
  end
95
168
 
169
+ # Intenral: Message handler routes received data to user defined
170
+ # 'on_message' method (if exists) and handles it's exceptions.
171
+ #
172
+ # data - The data to be handled.
173
+ #
96
174
  def message_handler(data)
97
175
  if respond_to?(:on_message)
98
176
  payload = JSON.parse(data.to_s)
@@ -102,15 +180,30 @@ module Kosmonaut
102
180
  exception_handler(err)
103
181
  end
104
182
 
183
+ # Internal: Handles given WebRocket error.
184
+ #
185
+ # errcode - A code of the received error.
186
+ #
187
+ # Raises Kosmonaut::UnauthorizerError if error 402 has been received.
105
188
  def error_handler(errcode)
106
189
  if respond_to?(:on_error)
107
190
  err = ERRORS[errcode.to_i]
108
- on_error(err ? err : UnknownServerError)
191
+ if err == UnauthorizedError
192
+ raise err.new
193
+ end
194
+ begin
195
+ on_error((err ? err : UnknownServerError).new)
196
+ rescue => err
197
+ exception_handler(err)
198
+ end
109
199
  end
110
- rescue => err
111
- exception_handler(err)
112
200
  end
113
201
 
202
+ # Internal: Exception handler passes given error to user defined
203
+ # exception handler or raises error if such not exists.
204
+ #
205
+ # err - The exception to be handled.
206
+ #
114
207
  def exception_handler(err)
115
208
  if respond_to?(:on_exception)
116
209
  on_exception(err)
@@ -2,15 +2,15 @@ require File.expand_path("../helper", __FILE__)
2
2
 
3
3
  class MyWorker < Kosmonaut::Worker
4
4
  def on_message(event, data)
5
- puts "MSG", event, data
5
+ puts "MSG : #{event} : #{data.inspect}"
6
6
  end
7
7
 
8
8
  def on_error(err)
9
- puts "ERR", err.to_s
9
+ puts "ERR : #{err.to_s}"
10
10
  end
11
11
 
12
12
  def on_exception(err)
13
- puts "EXC", err.to_s
13
+ puts "EXC : #{err.to_s}"
14
14
  end
15
15
  end
16
16
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: kosmonaut
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.3.0
5
+ version: 0.3.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Krzysztof Kowalik
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2012-01-24 00:00:00 Z
14
+ date: 2012-01-28 00:00:00 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: json
@@ -44,12 +44,12 @@ extensions: []
44
44
  extra_rdoc_files: []
45
45
 
46
46
  files:
47
- - lib/kosmonaut.rb
47
+ - lib/kosmonaut/client.rb
48
+ - lib/kosmonaut/errors.rb
48
49
  - lib/kosmonaut/socket.rb
49
- - lib/kosmonaut/worker.rb
50
50
  - lib/kosmonaut/version.rb
51
- - lib/kosmonaut/errors.rb
52
- - lib/kosmonaut/client.rb
51
+ - lib/kosmonaut/worker.rb
52
+ - lib/kosmonaut.rb
53
53
  - test/helper.rb
54
54
  - test/test_kosmonaut_client.rb
55
55
  - test/test_kosmonaut_worker.rb
@@ -82,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
82
82
  requirements: []
83
83
 
84
84
  rubyforge_project:
85
- rubygems_version: 1.8.15
85
+ rubygems_version: 1.8.10
86
86
  signing_key:
87
87
  specification_version: 3
88
88
  summary: Ruby client for the WebRocket backend