rjr 0.6.1 → 0.7.0

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/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  # rjr project Rakefile
2
2
  #
3
- # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
3
+ # Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  require 'rdoc/task'
7
7
  require "rspec/core/rake_task"
data/bin/rjr-server CHANGED
@@ -5,8 +5,8 @@
5
5
  # Flags:
6
6
  # -h --help
7
7
  #
8
- # Copyright (C) 2011 Mohammed Morsi <mo@morsi.org>
9
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
8
+ # Copyright (C) 2011-2012 Mohammed Morsi <mo@morsi.org>
9
+ # Licensed under the Apache License, Version 2.0
10
10
 
11
11
  require 'rubygems'
12
12
  require 'optparse'
data/lib/rjr/amqp_node.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # RJR AMQP Endpoint
2
2
  #
3
3
  # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  # establish client connection w/ specified args and invoke block w/
7
7
  # newly created client, returning it after block terminates
@@ -57,7 +57,6 @@ class AMQPNode < RJR::Node
57
57
  :rjr_callback =>
58
58
  AMQPNodeCallback.new(:node => self,
59
59
  :exchange => @exchange,
60
- :amqp_lock => @amqp_lock,
61
60
  :destination => reply_to,
62
61
  :headers => headers))
63
62
  response = ResponseMessage.new(:id => msg.msg_id, :result => result, :headers => headers)
@@ -74,8 +73,9 @@ class AMQPNode < RJR::Node
74
73
  end
75
74
 
76
75
  @response_lock.synchronize{
77
- @result = [res]
78
- @result << err if !err.nil?
76
+ result = [msg.msg_id, res]
77
+ result << err if !err.nil?
78
+ @responses << result
79
79
  @response_cv.signal
80
80
  }
81
81
  end
@@ -88,7 +88,10 @@ class AMQPNode < RJR::Node
88
88
  @broker = args[:broker]
89
89
  @connection_event_handlers = {:closed => [], :error => []}
90
90
  @response_lock = Mutex.new
91
- @response_cv = ConditionVariable.new
91
+ @response_cv = ConditionVariable.new
92
+ @response_check_cv = ConditionVariable.new
93
+ @responses = []
94
+ @amqp_lock = Mutex.new
92
95
  end
93
96
 
94
97
  # Initialize the amqp subsystem
@@ -106,11 +109,11 @@ class AMQPNode < RJR::Node
106
109
  @exchange = @channel.default_exchange
107
110
 
108
111
  @listening = false
109
- @disconnected = false
112
+ #@disconnected = false
110
113
 
111
114
  @exchange.on_return do |basic_return, metadata, payload|
112
115
  puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
113
- @disconnected = true # FIXME member will be set on wrong class
116
+ #@disconnected = true # FIXME member will be set on wrong class
114
117
  connection_event(:error)
115
118
  connection_event(:closed)
116
119
  end
@@ -118,25 +121,41 @@ class AMQPNode < RJR::Node
118
121
 
119
122
  # publish a message using the amqp exchange
120
123
  def publish(*args)
121
- raise RJR::Errors::ConnectionError.new("client unreachable") if @disconnected
122
- @exchange.publish *args
124
+ @amqp_lock.synchronize {
125
+ #raise RJR::Errors::ConnectionError.new("client unreachable") if @disconnected
126
+ @exchange.publish *args
127
+ }
128
+ nil
123
129
  end
124
130
 
125
131
  # subscribe to messages using the amqp queue
126
132
  def subscribe(*args, &bl)
127
133
  return if @listening
128
- @listening = true
129
- @queue.subscribe do |metadata, msg|
130
- bl.call metadata, msg
131
- end
134
+ @amqp_lock.synchronize {
135
+ @listening = true
136
+ @queue.subscribe do |metadata, msg|
137
+ bl.call metadata, msg
138
+ end
139
+ }
140
+ nil
132
141
  end
133
142
 
134
143
  def wait_for_result(message)
135
144
  res = nil
136
- @response_lock.synchronize{
137
- @response_cv.wait @response_lock
138
- res = @result
139
- }
145
+ while res.nil?
146
+ @response_lock.synchronize{
147
+ @response_cv.wait @response_lock
148
+ # FIXME throw err if more than 1 match found
149
+ res = @responses.select { |response| message.msg_id == response.first }.first
150
+ unless res.nil?
151
+ @responses.delete(res)
152
+ else
153
+ @response_cv.signal
154
+ @response_check_cv.wait @response_lock
155
+ end
156
+ @response_check_cv.signal
157
+ }
158
+ end
140
159
  return res
141
160
  end
142
161
 
@@ -185,14 +204,15 @@ class AMQPNode < RJR::Node
185
204
  publish message.to_s, :routing_key => routing_key, :reply_to => @queue_name
186
205
  end
187
206
 
207
+ # TODO optional timeout for response ?
188
208
  result = wait_for_result(message)
189
- self.stop
190
- self.join unless self.em_running?
209
+ #self.stop
210
+ #self.join unless self.em_running?
191
211
 
192
- if result.size > 1
193
- raise result[1]
212
+ if result.size > 2
213
+ raise result[2]
194
214
  end
195
- return result.first
215
+ return result[1]
196
216
  end
197
217
 
198
218
  end
data/lib/rjr/common.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # RJR Utility Methods
2
2
  #
3
3
  # Copyright (C) 2011 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  require 'logger'
7
7
 
@@ -1,7 +1,7 @@
1
1
  # RJR Request / Response Dispatcher
2
2
  #
3
3
  # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  # establish client connection w/ specified args and invoke block w/
7
7
  # newly created client, returning it after block terminates
data/lib/rjr/errors.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # RJR Errors
2
2
  #
3
3
  # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  # establish client connection w/ specified args and invoke block w/
7
7
  # newly created client, returning it after block terminates
@@ -1,7 +1,7 @@
1
1
  # RJR Local Endpoint
2
2
  #
3
3
  # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  # establish client connection w/ specified args and invoke block w/
7
7
  # newly created client, returning it after block terminates
@@ -19,6 +19,7 @@ class LocalNodeCallback
19
19
  end
20
20
 
21
21
  def invoke(callback_method, *data)
22
+ # TODO any exceptions from handler will propagate here, surround w/ begin/rescue block
22
23
  @node.invoke_request(callback_method, *data)
23
24
  # TODO support local_node 'disconnections'
24
25
  end
data/lib/rjr/message.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # RJR Message
2
2
  #
3
3
  # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  # establish client connection w/ specified args and invoke block w/
7
7
  # newly created client, returning it after block terminates
@@ -1,7 +1,7 @@
1
1
  # RJR MultiNode Endpoint
2
2
  #
3
3
  # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  # establish client connection w/ specified args and invoke block w/
7
7
  # newly created client, returning it after block terminates
data/lib/rjr/node.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # RJR Node
2
2
  #
3
3
  # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  # establish client connection w/ specified args and invoke block w/
7
7
  # newly created client, returning it after block terminates
@@ -20,11 +20,19 @@ class Node
20
20
  # attitional parameters to set on messages
21
21
  attr_accessor :message_headers
22
22
 
23
+ attr_reader :thread_pool
24
+
23
25
  def initialize(args = {})
24
26
  @node_id = args[:node_id]
25
27
 
26
28
  @message_headers = {}
27
29
  @message_headers.merge!(args[:headers]) if args.has_key?(:headers)
30
+
31
+ ObjectSpace.define_finalizer(self, self.class.finalize(self))
32
+ end
33
+
34
+ def self.finalize(node)
35
+ proc { node.halt ; node.join }
28
36
  end
29
37
 
30
38
  # run job in event machine
@@ -46,7 +54,7 @@ class Node
46
54
  begin
47
55
  EventMachine.run
48
56
  rescue Exception => e
49
- puts "Critical exception #{e}"
57
+ puts "Critical exception #{e}\n#{e.backtrace.join("\n")}"
50
58
  ensure
51
59
  end
52
60
  }
@@ -60,10 +68,10 @@ class Node
60
68
  end
61
69
 
62
70
  def join
63
- if @@em_thread
64
- @@em_thread.join
65
- @@em_thread = nil
66
- end
71
+ @@em_thread.join if @@em_thread
72
+ @@em_thread = nil
73
+ @thread_pool.join if @thread_pool
74
+ @thread_pool = nil
67
75
  end
68
76
 
69
77
  def stop
@@ -77,6 +85,7 @@ class Node
77
85
  def halt
78
86
  @@em_jobs = 0
79
87
  EventMachine.stop
88
+ @thread_pool.stop unless @thread_pool.nil?
80
89
  end
81
90
 
82
91
  end
data/lib/rjr/tcp_node.rb CHANGED
@@ -1 +1,174 @@
1
- # TODO
1
+ # RJR TCP Endpoint
2
+ #
3
+ # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
+
6
+ require 'uri'
7
+ require 'eventmachine'
8
+
9
+ require 'rjr/node'
10
+ require 'rjr/message'
11
+
12
+ module RJR
13
+
14
+ # TCP client node callback interface,
15
+ # send data back to client via established tcp socket.
16
+ class TCPNodeCallback
17
+ def initialize(args = {})
18
+ @endpoint = args[:endpoint]
19
+ @message_headers = args[:headers]
20
+ end
21
+
22
+ def invoke(callback_method, *data)
23
+ msg = RequestMessage.new :method => callback_method, :args => data, :headers => @message_headers
24
+ # TODO surround w/ begin/rescue block incase of socket errors
25
+ @endpoint.send_data msg.to_s
26
+ end
27
+ end
28
+
29
+ # helper class intialized by event machine corresponding to
30
+ # a client or server socket connection
31
+ class TCPNodeEndpoint < EventMachine::Connection
32
+ def initialize(args = {})
33
+ @rjr_node = args[:rjr_node]
34
+
35
+ # these params should be set for clients
36
+ @send_message = args[:init_message]
37
+ end
38
+
39
+ def post_init
40
+ unless @send_message.nil?
41
+ send_data @send_message.to_s
42
+ @send_message = nil
43
+ end
44
+ end
45
+
46
+ def receive_data(data)
47
+ if RequestMessage.is_request_message?(data)
48
+ @rjr_node.thread_pool << ThreadPoolJob.new { handle_request(data) }
49
+
50
+ elsif ResponseMessage.is_response_message?(data)
51
+ handle_response(data)
52
+
53
+ end
54
+ end
55
+
56
+
57
+ def handle_request(data)
58
+ client_port, client_ip = Socket.unpack_sockaddr_in(get_peername)
59
+ msg = RequestMessage.new(:message => data, :headers => @rjr_node.message_headers)
60
+ headers = @rjr_node.message_headers.merge(msg.headers)
61
+ result = Dispatcher.dispatch_request(msg.jr_method,
62
+ :method_args => msg.jr_args,
63
+ :headers => headers,
64
+ :client_ip => client_ip,
65
+ :client_port => client_port,
66
+ :rjr_node => @rjr_node,
67
+ :rjr_node_id => @rjr_node.node_id,
68
+ :rjr_node_type => TCPNode::RJR_NODE_TYPE,
69
+ :rjr_callback =>
70
+ TCPNodeCallback.new(:endpoint => self,
71
+ :headers => headers))
72
+ response = ResponseMessage.new(:id => msg.msg_id, :result => result, :headers => headers)
73
+ send_data(response.to_s)
74
+ end
75
+
76
+ def handle_response(data)
77
+ msg = ResponseMessage.new(:message => data, :headers => @rjr_node.message_headers)
78
+ res = err = nil
79
+ begin
80
+ res = Dispatcher.handle_response(msg.result)
81
+ rescue Exception => e
82
+ err = e
83
+ end
84
+
85
+ @rjr_node.response_lock.synchronize {
86
+ @rjr_node.responses << [msg.msg_id, res]
87
+ @rjr_node.responses.last << err unless err.nil?
88
+ @rjr_node.response_cv.signal
89
+ }
90
+ end
91
+ end
92
+
93
+ # TCP node definition, listen for and invoke json-rpc requests via tcp sockets
94
+ class TCPNode < RJR::Node
95
+ RJR_NODE_TYPE = :tcp
96
+
97
+ attr_accessor :response_lock
98
+ attr_accessor :response_cv
99
+ attr_accessor :responses
100
+
101
+ public
102
+ # initialize the node w/ the specified params
103
+ def initialize(args = {})
104
+ super(args)
105
+ @host = args[:host]
106
+ @port = args[:port]
107
+
108
+ @response_lock = Mutex.new
109
+ @response_cv = ConditionVariable.new
110
+ @response_cv = ConditionVariable.new
111
+ @response_check_cv = ConditionVariable.new
112
+ @responses = []
113
+
114
+ @connection_event_handlers = {:closed => [], :error => []}
115
+ end
116
+
117
+ # register connection event handler
118
+ def on(event, &handler)
119
+ if @connection_event_handlers.keys.include?(event)
120
+ @connection_event_handlers[event] << handler
121
+ end
122
+ end
123
+
124
+ # Initialize the tcp subsystem
125
+ def init_node
126
+ end
127
+
128
+ # Instruct Node to start listening for and dispatching rpc requests
129
+ def listen
130
+ em_run {
131
+ init_node
132
+ EventMachine::start_server @host, @port, TCPNodeEndpoint, { :rjr_node => self }
133
+ }
134
+ end
135
+
136
+ # Instructs node to send rpc request, and wait for / return response
137
+ def invoke_request(uri, rpc_method, *args)
138
+ uri = URI.parse(uri)
139
+ host,port = uri.host, uri.port
140
+
141
+ message = RequestMessage.new :method => rpc_method,
142
+ :args => args,
143
+ :headers => @message_headers
144
+ em_run{
145
+ init_node
146
+ EventMachine::connect host, port, TCPNodeEndpoint, { :rjr_node => self,
147
+ :init_message => message }
148
+ }
149
+
150
+ # wait for matching response
151
+ res = nil
152
+ while res.nil?
153
+ @response_lock.synchronize {
154
+ @response_cv.wait response_lock
155
+ res = @responses.select { |response| message.msg_id == response.first }.first
156
+ unless res.nil?
157
+ @responses.delete(res)
158
+ else
159
+ @response_cv.signal
160
+ @response_check_cv.wait @response_lock
161
+ end
162
+ @response_check_cv.signal
163
+ }
164
+ end
165
+
166
+ # raise error or return result
167
+ if res.size > 2
168
+ raise res[2]
169
+ end
170
+ return res[1]
171
+ end
172
+ end
173
+
174
+ end # module RJR
@@ -1,27 +1,7 @@
1
1
  # Thread Pool
2
2
  #
3
- # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person
6
- # obtaining a copy of this software and associated documentation
7
- # files (the "Software"), to deal in the Software without
8
- # restriction, including without limitation the rights to use,
9
- # copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- # copies of the Software, and to permit persons to whom the
11
- # Software is furnished to do so, subject to the following
12
- # conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be
15
- # included in all copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
- # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
- # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
- # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
- # OTHER DEALINGS IN THE SOFTWARE.
3
+ # Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
25
5
 
26
6
  # Work item to be executed in thread pool
27
7
  class ThreadPoolJob
@@ -72,7 +52,7 @@ class ThreadPool
72
52
  def running?
73
53
  res = nil
74
54
  @thread_lock.synchronize{
75
- res = (@thread.status != false)
55
+ res = (!@thread.nil? && (@thread.status != false))
76
56
  }
77
57
  res
78
58
  end
@@ -93,6 +73,13 @@ class ThreadPool
93
73
  @thread.kill
94
74
  @thread.join
95
75
  end
76
+ @thread = nil
77
+ }
78
+ end
79
+
80
+ def join
81
+ @thread_lock.synchronize {
82
+ @thread.join unless @thread.nil?
96
83
  }
97
84
  end
98
85
  end
@@ -130,7 +117,7 @@ class ThreadPool
130
117
  end
131
118
 
132
119
  def running?
133
- !terminate && (@timeout.nil? || @timeout_thread.status) &&
120
+ !terminate && (@timeout.nil? || (!@timeout_thread.nil? && @timeout_thread.status)) &&
134
121
  @job_runners.all? { |r| r.running? }
135
122
  end
136
123
 
@@ -157,9 +144,17 @@ class ThreadPool
157
144
  # Terminate the thread pool
158
145
  def stop
159
146
  terminate = true
160
- @timeout_thread.join unless @timout_thread.nil?
147
+ unless @timout_thread.nil?
148
+ @timeout_thread.join
149
+ @timeout_thread.terminate
150
+ end
151
+ @timeout_thread = nil
161
152
  @work_queue.clear
162
153
  @job_runners_lock.synchronize { @job_runners.each { |jr| jr.stop } }
163
154
  end
155
+
156
+ def join
157
+ @job_runners_lock.synchronize { @job_runners.each { |jr| jr.join } }
158
+ end
164
159
  end
165
160
 
data/lib/rjr/web_node.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # RJR WWW Endpoint
2
2
  #
3
3
  # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  # establish client connection w/ specified args and invoke block w/
7
7
  # newly created client, returning it after block terminates
data/lib/rjr/ws_node.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # RJR WebSockets Endpoint
2
2
  #
3
3
  # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  # establish client connection w/ specified args and invoke block w/
7
7
  # newly created client, returning it after block terminates
@@ -26,6 +26,7 @@ class WSNodeCallback
26
26
  #msg = CallbackMessage.new(:data => data)
27
27
  msg = RequestMessage.new :method => callback_method, :args => data, :headers => @message_headers
28
28
  raise RJR::Errors::ConnectionError.new("websocket closed") if @socket.state == :closed
29
+ # TODO surround w/ begin/rescue block incase of other socket errors?
29
30
  @socket.send(msg.to_s)
30
31
  end
31
32
  end
data/lib/rjr.rb CHANGED
@@ -1,7 +1,7 @@
1
- # json-rpc over qpid
1
+ # rjr - Ruby Json Rpc
2
2
  #
3
- # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
- # Licensed under the AGPLv3+ http://www.gnu.org/licenses/agpl.txt
3
+ # Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
5
 
6
6
  require 'rjr/common'
7
7
  require 'rjr/errors'
@@ -14,4 +14,5 @@ require 'rjr/local_node'
14
14
  require 'rjr/amqp_node'
15
15
  require 'rjr/ws_node'
16
16
  require 'rjr/web_node'
17
+ require 'rjr/tcp_node'
17
18
  require 'rjr/multi_node'
@@ -0,0 +1,32 @@
1
+ require 'rjr/tcp_node'
2
+ require 'rjr/dispatcher'
3
+
4
+ describe RJR::TCPNode do
5
+ it "should invoke and satisfy tcp requests" do
6
+ server = RJR::TCPNode.new :node_id => 'tcp', :host => 'localhost', :port => 9987
7
+ client = RJR::TCPNode.new
8
+
9
+ foobar_invoked = false
10
+ RJR::Dispatcher.init_handlers
11
+ RJR::Dispatcher.add_handler('foobar') { |param|
12
+ @client_ip.should == "127.0.0.1"
13
+ #@client_port.should == 9987
14
+ @rjr_node.should == server
15
+ @rjr_node_id.should == 'tcp'
16
+ @rjr_node_type.should == :tcp
17
+ param.should == 'myparam'
18
+ foobar_invoked = true
19
+ 'retval'
20
+ }
21
+
22
+ server.listen
23
+ sleep 1
24
+ res = client.invoke_request 'jsonrpc://localhost:9987', 'foobar', 'myparam'
25
+ res.should == 'retval'
26
+ server.halt
27
+ server.join
28
+ foobar_invoked.should == true
29
+ end
30
+
31
+ # TODO ensure closed / error event handlers are invoked
32
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rjr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-25 00:00:00.000000000 Z
12
+ date: 2012-08-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -53,6 +53,7 @@ files:
53
53
  - specs/dispatcher_spec.rb
54
54
  - specs/node_spec.rb
55
55
  - specs/amqp_node_spec.rb
56
+ - specs/tcp_node_spec.rb
56
57
  - specs/multi_node_spec.rb
57
58
  - specs/local_node_spec.rb
58
59
  - specs/web_node_spec.rb