ezmobius-nanite 0.4.1.1 → 0.4.1.2

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
@@ -43,6 +43,8 @@ Rake::GemPackageTask.new(spec) do |pkg|
43
43
  pkg.gem_spec = spec
44
44
  end
45
45
 
46
+ task :default => :spec
47
+
46
48
  task :install => [:package] do
47
49
  sh %{sudo gem install pkg/#{GEM}-#{VER}}
48
50
  end
@@ -8,7 +8,9 @@ module Nanite
8
8
 
9
9
  def register(actor, prefix)
10
10
  raise ArgumentError, "#{actor.inspect} is not a Nanite::Actor subclass instance" unless Nanite::Actor === actor
11
- Nanite::Log.info("Registering #{actor.inspect} with prefix #{prefix.inspect}")
11
+ log_msg = "[actor] #{actor.class.to_s}"
12
+ log_msg += ", prefix #{prefix}" if prefix && !prefix.empty?
13
+ Nanite::Log.info(log_msg)
12
14
  prefix ||= actor.class.default_prefix
13
15
  actors[prefix.to_s] = actor
14
16
  end
data/lib/nanite/agent.rb CHANGED
@@ -158,7 +158,7 @@ module Nanite
158
158
  actors = @options[:actors]
159
159
  Dir["#{actors_dir}/*.rb"].each do |actor|
160
160
  next if actors && !actors.include?(File.basename(actor, ".rb"))
161
- Nanite::Log.info("loading actor: #{actor}")
161
+ Nanite::Log.info("[setup] loading #{actor}")
162
162
  require actor
163
163
  end
164
164
  init_path = @options[:initrb] || File.join(options[:root], 'init.rb')
@@ -166,25 +166,27 @@ module Nanite
166
166
  end
167
167
 
168
168
  def receive(packet)
169
+ Nanite::Log.debug("RECV #{packet.to_s}")
169
170
  case packet
170
171
  when Advertise
171
- Nanite::Log.debug("handling Advertise: #{packet.inspect}")
172
+ Nanite::Log.info("RECV #{packet.to_s}") unless Nanite::Log.level == Logger::DEBUG
172
173
  advertise_services
173
174
  when Request, Push
174
- Nanite::Log.debug("handling Request: #{packet.inspect}")
175
175
  if @security && !@security.authorize(packet)
176
+ Nanite::Log.warn("RECV NOT AUTHORIZED #{packet.to_s}")
176
177
  if packet.kind_of?(Request)
177
178
  r = Result.new(packet.token, packet.reply_to, @deny_token, identity)
178
179
  amq.queue(packet.reply_to, :no_declare => options[:secure]).publish(serializer.dump(r))
179
180
  end
180
181
  else
182
+ Nanite::Log.info("RECV #{packet.to_s([:from, :tags])}") unless Nanite::Log.level == Logger::DEBUG
181
183
  dispatcher.dispatch(packet)
182
184
  end
183
185
  when Result
184
- Nanite::Log.debug("handling Result: #{packet.inspect}")
186
+ Nanite::Log.info("RECV #{packet.to_s([])}") unless Nanite::Log.level == Logger::DEBUG
185
187
  @mapper_proxy.handle_result(packet)
186
188
  when IntermediateMessage
187
- Nanite::Log.debug("handling Intermediate Result: #{packet.inspect}")
189
+ Nanite::Log.info("RECV #{packet.to_s([])}") unless Nanite::Log.level == Logger::DEBUG
188
190
  @mapper_proxy.handle_intermediate_result(packet)
189
191
  end
190
192
  end
@@ -198,10 +200,9 @@ module Nanite
198
200
  amq.queue(identity, :durable => true).subscribe(:ack => true) do |info, msg|
199
201
  begin
200
202
  info.ack
201
- packet = serializer.load(msg)
202
- receive(packet)
203
+ receive(serializer.load(msg))
203
204
  rescue Exception => e
204
- Nanite::Log.error("Error handling packet: #{e.message}")
205
+ Nanite::Log.error("RECV #{e.message}")
205
206
  end
206
207
  end
207
208
  end
@@ -231,13 +232,15 @@ module Nanite
231
232
  def un_register
232
233
  unless @unregistered
233
234
  @unregistered = true
235
+ Nanite::Log.info("SEND [un_register]")
234
236
  amq.fanout('registration', :no_declare => options[:secure]).publish(serializer.dump(UnRegister.new(identity)))
235
237
  end
236
238
  end
237
239
 
238
240
  def advertise_services
239
- Nanite::Log.debug("advertise_services: #{registry.services.inspect}")
240
- amq.fanout('registration', :no_declare => options[:secure]).publish(serializer.dump(Register.new(identity, registry.services, status_proc.call, self.tags)))
241
+ reg = Register.new(identity, registry.services, status_proc.call, self.tags)
242
+ Nanite::Log.info("SEND #{reg.to_s}")
243
+ amq.fanout('registration', :no_declare => options[:secure]).publish(serializer.dump(reg))
241
244
  end
242
245
 
243
246
  def parse_uptime(up)
@@ -1,23 +1,17 @@
1
1
  module Nanite
2
2
  class Cluster
3
- attr_reader :agent_timeout, :nanites, :reaper, :serializer, :identity, :amq, :redis, :mapper
3
+ attr_reader :agent_timeout, :nanites, :reaper, :serializer, :identity, :amq, :redis, :mapper, :callbacks
4
4
 
5
- def initialize(amq, agent_timeout, identity, serializer, mapper, redis=nil)
5
+ def initialize(amq, agent_timeout, identity, serializer, mapper, state_configuration=nil, callbacks = {})
6
6
  @amq = amq
7
7
  @agent_timeout = agent_timeout
8
8
  @identity = identity
9
9
  @serializer = serializer
10
10
  @mapper = mapper
11
- @redis = redis
11
+ @state = state_configuration
12
12
  @security = SecurityProvider.get
13
- if redis
14
- Nanite::Log.info("using redis for state storage")
15
- require 'nanite/state'
16
- @nanites = ::Nanite::State.new(redis)
17
- else
18
- require 'nanite/local_state'
19
- @nanites = Nanite::LocalState.new
20
- end
13
+ @callbacks = callbacks
14
+ setup_state
21
15
  @reaper = Reaper.new(agent_timeout)
22
16
  setup_queues
23
17
  end
@@ -35,18 +29,27 @@ module Nanite
35
29
  case reg
36
30
  when Register
37
31
  if @security.authorize_registration(reg)
32
+ Nanite::Log.info("RECV #{reg.to_s}")
38
33
  nanites[reg.identity] = { :services => reg.services, :status => reg.status, :tags => reg.tags }
39
- reaper.timeout(reg.identity, agent_timeout + 1) { nanites.delete(reg.identity) }
40
- Nanite::Log.info("registered: #{reg.identity}, #{nanites[reg.identity].inspect}")
34
+ reaper.timeout(reg.identity, agent_timeout + 1) { nanite_timed_out(reg.identity) }
35
+ callbacks[:register].call(reg.identity, mapper) if callbacks[:register]
41
36
  else
42
- Nanite::Log.warning("registration of #{reg.inspect} not authorized")
37
+ Nanite::Log.warn("RECV NOT AUTHORIZED #{reg.to_s}")
43
38
  end
44
39
  when UnRegister
40
+ Nanite::Log.info("RECV #{reg.to_s}")
45
41
  nanites.delete(reg.identity)
46
- Nanite::Log.info("un-registering: #{reg.identity}")
42
+ callbacks[:unregister].call(reg.identity, mapper) if callbacks[:unregister]
43
+ else
44
+ Nanite::Log.warn("RECV [register] Invalid packet type: #{reg.class}")
47
45
  end
48
46
  end
49
47
 
48
+ def nanite_timed_out(token)
49
+ nanite = nanites.delete(token)
50
+ callbacks[:timeout].call(token, mapper) if callbacks[:timeout]
51
+ end
52
+
50
53
  def route(request, targets)
51
54
  EM.next_tick { targets.map { |target| publish(request, target) } }
52
55
  end
@@ -57,6 +60,7 @@ module Nanite
57
60
  begin
58
61
  old_target = request.target
59
62
  request.target = target unless target == 'mapper-offline'
63
+ Nanite::Log.info("SEND #{request.to_s([:from, :tags, :target])}")
60
64
  amq.queue(target).publish(serializer.dump(request), :persistent => request.persistent)
61
65
  ensure
62
66
  request.target = old_target
@@ -68,36 +72,46 @@ module Nanite
68
72
  # updates nanite information (last ping timestamps, status)
69
73
  # when heartbeat message is received
70
74
  def handle_ping(ping)
71
- if nanite = nanites[ping.identity]
72
- nanite[:status] = ping.status
73
- reaper.reset_with_autoregister_hack(ping.identity, agent_timeout + 1) { nanites.delete(ping.identity) }
74
- else
75
- amq.queue(ping.identity).publish(serializer.dump(Advertise.new))
75
+ begin
76
+ if nanite = nanites[ping.identity]
77
+ nanite[:status] = ping.status
78
+ reaper.reset_with_autoregister_hack(ping.identity, agent_timeout + 1) { nanite_timed_out(ping.identity) }
79
+ else
80
+ packet = Advertise.new
81
+ Nanite::Log.info("SEND #{packet.to_s} to #{ping.identity}")
82
+ amq.queue(ping.identity).publish(serializer.dump(packet))
83
+ end
76
84
  end
77
85
  end
78
86
 
79
87
  # forward request coming from agent
80
88
  def handle_request(request)
81
89
  if @security.authorize_request(request)
82
- result = Result.new(request.token, request.from, nil, mapper.identity)
83
- intm_handler = lambda do |res|
84
- result.results = res
90
+ Nanite::Log.info("RECV #{request.to_s([:from, :target, :tags])}") unless Nanite::Log.level == Logger::DEBUG
91
+ Nanite::Log.debug("RECV #{request.to_s}")
92
+
93
+ intm_handler = lambda do |result, job|
94
+ result = IntermediateMessage.new(request.token, job.request.from, mapper.identity, nil, result)
85
95
  forward_response(result, request.persistent)
86
96
  end
97
+
98
+ result = Result.new(request.token, request.from, nil, mapper.identity)
87
99
  ok = mapper.send_request(request, :intermediate_handler => intm_handler) do |res|
88
100
  result.results = res
89
101
  forward_response(result, request.persistent)
90
102
  end
103
+
91
104
  if ok == false
92
105
  forward_response(result, request.persistent)
93
106
  end
94
107
  else
95
- Nanite::Log.warning("request #{request.inspect} not authorized")
108
+ Nanite::Log.warn("RECV NOT AUTHORIZED #{request.to_s}")
96
109
  end
97
110
  end
98
111
 
99
112
  # forward response back to agent that originally made the request
100
113
  def forward_response(res, persistent)
114
+ Nanite::Log.info("SEND #{res.to_s([:to])}")
101
115
  amq.queue(res.to).publish(serializer.dump(res), :persistent => persistent)
102
116
  end
103
117
 
@@ -150,14 +164,14 @@ module Nanite
150
164
  handler = lambda do |ping|
151
165
  begin
152
166
  ping = serializer.load(ping)
153
- Nanite::Log.debug("got heartbeat from #{ping.identity}") if ping.respond_to?(:identity)
167
+ Nanite::Log.debug("RECV #{ping.to_s}") if ping.respond_to?(:to_s)
154
168
  handle_ping(ping)
155
169
  rescue Exception => e
156
- Nanite::Log.error("Error handling heartbeat: #{e.message}")
170
+ Nanite::Log.error("RECV [ping] #{e.message}")
157
171
  end
158
172
  end
159
173
  hb_fanout = amq.fanout('heartbeat', :durable => true)
160
- if @redis
174
+ if shared_state?
161
175
  amq.queue("heartbeat").bind(hb_fanout).subscribe &handler
162
176
  else
163
177
  amq.queue("heartbeat-#{identity}", :exclusive => true).bind(hb_fanout).subscribe &handler
@@ -167,15 +181,13 @@ module Nanite
167
181
  def setup_registration_queue
168
182
  handler = lambda do |msg|
169
183
  begin
170
- msg = serializer.load(msg)
171
- Nanite::Log.debug("got registration from #{msg.identity}")
172
- register(msg)
184
+ register(serializer.load(msg))
173
185
  rescue Exception => e
174
- Nanite::Log.error("Error handling registration: #{e.message}")
186
+ Nanite::Log.error("RECV [register] #{e.message}")
175
187
  end
176
188
  end
177
189
  reg_fanout = amq.fanout('registration', :durable => true)
178
- if @redis
190
+ if shared_state?
179
191
  amq.queue("registration").bind(reg_fanout).subscribe &handler
180
192
  else
181
193
  amq.queue("registration-#{identity}", :exclusive => true).bind(reg_fanout).subscribe &handler
@@ -185,19 +197,36 @@ module Nanite
185
197
  def setup_request_queue
186
198
  handler = lambda do |msg|
187
199
  begin
188
- msg = serializer.load(msg)
189
- Nanite::Log.debug("got request from #{msg.from} of type #{msg.type}")
190
- handle_request(msg)
200
+ handle_request(serializer.load(msg))
191
201
  rescue Exception => e
192
- Nanite::Log.error("Error handling request: #{e.message}")
202
+ Nanite::Log.error("RECV [request] #{e.message}")
193
203
  end
194
204
  end
195
205
  req_fanout = amq.fanout('request', :durable => true)
196
- if @redis
206
+ if shared_state?
197
207
  amq.queue("request").bind(req_fanout).subscribe &handler
198
208
  else
199
209
  amq.queue("request-#{identity}", :exclusive => true).bind(req_fanout).subscribe &handler
200
210
  end
201
211
  end
212
+
213
+ def setup_state
214
+ case @state
215
+ when String
216
+ # backwards compatibility, we assume redis if the configuration option
217
+ # was a string
218
+ Nanite::Log.info("[setup] using redis for state storage")
219
+ require 'nanite/state'
220
+ @nanites = Nanite::State.new(@state)
221
+ when Hash
222
+ else
223
+ require 'nanite/local_state'
224
+ @nanites = Nanite::LocalState.new
225
+ end
226
+ end
227
+
228
+ def shared_state?
229
+ !@state.nil?
230
+ end
202
231
  end
203
232
  end
@@ -31,6 +31,7 @@ module Nanite
31
31
  callback = lambda do |r|
32
32
  if deliverable.kind_of?(Request)
33
33
  r = Result.new(deliverable.token, deliverable.reply_to, r, identity)
34
+ Nanite::Log.info("SEND #{r.to_s([])}")
34
35
  amq.queue(deliverable.reply_to, :no_declare => options[:secure]).publish(serializer.dump(r))
35
36
  end
36
37
  r # For unit tests
data/lib/nanite/job.rb CHANGED
@@ -14,8 +14,6 @@ module Nanite
14
14
  end
15
15
 
16
16
  def process(msg)
17
- Nanite::Log.debug("processing message: #{msg.inspect}")
18
-
19
17
  if job = jobs[msg.token]
20
18
  job.process(msg)
21
19
 
@@ -29,6 +27,8 @@ module Nanite
29
27
  handler = job.intermediate_handler_for_key(key)
30
28
  if handler
31
29
  case handler.arity
30
+ when 2
31
+ handler.call(job.intermediate_state[msg.from][key].last, job)
32
32
  when 3
33
33
  handler.call(key, msg.from, job.intermediate_state[msg.from][key].last)
34
34
  when 4
data/lib/nanite/log.rb CHANGED
@@ -13,7 +13,7 @@ module Nanite
13
13
  # Use Nanite::Logger.init when you want to set up the logger manually.
14
14
  # If this method is called with no arguments, it will log to STDOUT at the :info level.
15
15
  # It also configures the Logger instance it creates to use the custom Nanite::Log::Formatter class.
16
- def init(identity, path = false)
16
+ def init(identity = nil, path = false)
17
17
  if path
18
18
  @file = File.join(path, "nanite.#{identity}.log")
19
19
  else
@@ -30,7 +30,8 @@ module Nanite
30
30
  def level=(loglevel)
31
31
  init() unless @logger
32
32
  loglevel = loglevel.intern if loglevel.is_a?(String)
33
- @logger.info("Setting log level to #{loglevel.to_s.upcase}")
33
+ @logger.info("[setup] setting log level to #{loglevel.to_s.upcase}")
34
+ @level = loglevel
34
35
  case loglevel
35
36
  when :debug
36
37
  @logger.level = Logger::DEBUG
@@ -46,12 +47,12 @@ module Nanite
46
47
  raise ArgumentError, "Log level must be one of :debug, :info, :warn, :error, or :fatal"
47
48
  end
48
49
  end
49
-
50
+
50
51
  # Passes any other method calls on directly to the underlying Logger object created with init. If
51
52
  # this method gets hit before a call to Nanite::Logger.init has been made, it will call
52
53
  # Nanite::Logger.init() with no arguments.
53
54
  def method_missing(method_symbol, *args)
54
- init(identity) unless @logger
55
+ init unless @logger
55
56
  if args.length > 0
56
57
  @logger.send(method_symbol, *args)
57
58
  else
data/lib/nanite/mapper.rb CHANGED
@@ -24,7 +24,8 @@ module Nanite
24
24
  attr_reader :cluster, :identity, :job_warden, :options, :serializer, :amq
25
25
 
26
26
  DEFAULT_OPTIONS = COMMON_DEFAULT_OPTIONS.merge({:user => 'mapper', :identity => Identity.generate, :agent_timeout => 15,
27
- :offline_redelivery_frequency => 10, :persistent => false, :offline_failsafe => false}) unless defined?(DEFAULT_OPTIONS)
27
+ :offline_redelivery_frequency => 10, :persistent => false, :offline_failsafe => false,
28
+ :callbacks => {} }) unless defined?(DEFAULT_OPTIONS)
28
29
 
29
30
  # Initializes a new mapper and establishes
30
31
  # AMQP connection. This must be used inside EM.run block or if EventMachine reactor
@@ -65,8 +66,9 @@ module Nanite
65
66
  #
66
67
  # persistent : true instructs the AMQP broker to save messages to persistent storage so that they aren't lost when the
67
68
  # broker is restarted. Default is false. Can be overriden on a per-message basis using the request and push methods.
68
- #
69
+ #
69
70
  # secure : use Security features of rabbitmq to restrict nanites to themselves
71
+ # prefetch : Sets prefetch (only supported in RabbitMQ >= 1.6)
70
72
  #
71
73
  # Connection options:
72
74
  #
@@ -105,14 +107,9 @@ module Nanite
105
107
  @options[:file_root] ||= File.join(@options[:root], 'files')
106
108
  @options.freeze
107
109
  end
108
-
110
+
109
111
  def run
110
- log_path = false
111
- if @options[:daemonize]
112
- log_path = (@options[:log_dir] || @options[:root] || Dir.pwd)
113
- end
114
- Nanite::Log.init(@identity, log_path)
115
- Nanite::Log.level = @options[:log_level] if @options[:log_level]
112
+ setup_logging
116
113
  @serializer = Serializer.new(@options[:format])
117
114
  pid_file = PidFile.new(@identity, @options)
118
115
  pid_file.check
@@ -120,11 +117,13 @@ module Nanite
120
117
  daemonize
121
118
  pid_file.write
122
119
  at_exit { pid_file.remove }
120
+ else
121
+ trap("INT") {exit}
123
122
  end
124
123
  @amq = start_amqp(@options)
125
124
  @job_warden = JobWarden.new(@serializer)
126
- @cluster = Cluster.new(@amq, @options[:agent_timeout], @options[:identity], @serializer, self, @options[:redis])
127
- Nanite::Log.info('starting mapper')
125
+ setup_cluster
126
+ Nanite::Log.info('[setup] starting mapper')
128
127
  setup_queues
129
128
  start_console if @options[:console] && !@options[:daemonize]
130
129
  end
@@ -189,7 +188,7 @@ module Nanite
189
188
  false
190
189
  end
191
190
  end
192
-
191
+
193
192
  # Make a nanite request which does not expect a response.
194
193
  #
195
194
  # ==== Parameters
@@ -235,6 +234,10 @@ module Nanite
235
234
  end
236
235
 
237
236
  def setup_queues
237
+ if amq.respond_to?(:prefetch) && @options.has_key?(:prefetch)
238
+ amq.prefetch(@options[:prefetch])
239
+ end
240
+
238
241
  setup_offline_queue
239
242
  setup_message_queue
240
243
  end
@@ -264,14 +267,28 @@ module Nanite
264
267
  def setup_message_queue
265
268
  amq.queue(identity, :exclusive => true).bind(amq.fanout(identity)).subscribe do |msg|
266
269
  begin
267
- msg = serializer.load(msg)
268
- Nanite::Log.debug("got result from #{msg.from}: #{msg.results.inspect}")
270
+ msg = serializer.load(msg)
271
+ Nanite::Log.debug("RECV #{msg.to_s}")
272
+ Nanite::Log.info("RECV #{msg.to_s([:from])}") unless Nanite::Log.level == Logger::DEBUG
269
273
  job_warden.process(msg)
270
274
  rescue Exception => e
271
- Nanite::Log.error("Error handling result: #{e.message}")
275
+ Nanite::Log.error("RECV [result] #{e.message}")
272
276
  end
273
277
  end
274
278
  end
279
+
280
+ def setup_logging
281
+ log_path = false
282
+ if @options[:daemonize]
283
+ log_path = (@options[:log_dir] || @options[:root] || Dir.pwd)
284
+ end
285
+ Nanite::Log.init(@identity, log_path)
286
+ Nanite::Log.level = @options[:log_level] if @options[:log_level]
287
+ end
288
+
289
+ def setup_cluster
290
+ @cluster = Cluster.new(@amq, @options[:agent_timeout], @options[:identity], @serializer, self, @options[:redis], @options[:callbacks])
291
+ end
275
292
  end
276
293
  end
277
294
 
@@ -37,6 +37,7 @@ module Nanite
37
37
  request.persistent = opts.key?(:persistent) ? opts[:persistent] : options[:persistent]
38
38
  pending_requests[request.token] =
39
39
  { :intermediate_handler => opts[:intermediate_handler], :result_handler => blk }
40
+ Nanite::Log.info("SEND #{request.to_s([:tags, :target])}")
40
41
  amqp.fanout('request', :no_declare => options[:secure]).publish(serializer.dump(request))
41
42
  end
42
43
 
@@ -2,58 +2,111 @@ module Nanite
2
2
  # Base class for all Nanite packets,
3
3
  # knows how to dump itself to JSON
4
4
  class Packet
5
+
6
+ attr_accessor :size
7
+
5
8
  def initialize
6
9
  raise NotImplementedError.new("#{self.class.name} is an abstract class.")
7
10
  end
11
+
8
12
  def to_json(*a)
9
- {
13
+ js = {
10
14
  'json_class' => self.class.name,
11
15
  'data' => instance_variables.inject({}) {|m,ivar| m[ivar.sub(/@/,'')] = instance_variable_get(ivar); m }
12
16
  }.to_json(*a)
17
+ js = js.chop + ",\"size\":#{js.size}}"
18
+ js
19
+ end
20
+
21
+ # Log representation
22
+ def to_s(filter=nil)
23
+ res = "[#{ self.class.to_s.split('::').last.
24
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
25
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
26
+ downcase }]"
27
+ res += " (#{size.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")} bytes)" if size && !size.to_s.empty?
28
+ res
29
+ end
30
+
31
+ # Log friendly name for given agent id
32
+ def id_to_s(id)
33
+ case id
34
+ when /^mapper-/ then 'mapper'
35
+ when /^nanite-(.*)/ then Regexp.last_match(1)
36
+ else id
37
+ end
38
+ end
39
+
40
+ # Wrap given string to given maximum number of characters per line
41
+ def wrap(txt, col=120)
42
+ txt.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/, "\\1\\3\n").chomp
13
43
  end
44
+
14
45
  end
15
46
 
16
47
  # packet that means start of a file transfer
17
48
  # operation
18
49
  class FileStart < Packet
50
+
19
51
  attr_accessor :filename, :token, :dest
20
- def initialize(filename, dest, token)
52
+
53
+ def initialize(filename, dest, token, size=nil)
21
54
  @filename = filename
22
55
  @dest = dest
23
56
  @token = token
57
+ @size = size
24
58
  end
25
59
 
26
60
  def self.json_create(o)
27
61
  i = o['data']
28
- new(i['filename'], i['dest'], i['token'])
62
+ new(i['filename'], i['dest'], i['token'], o['size'])
63
+ end
64
+
65
+ def to_s
66
+ wrap("#{super} <#{token}> #{filename} to #{dest}")
29
67
  end
30
68
  end
31
69
 
32
70
  # packet that means end of a file transfer
33
71
  # operation
34
72
  class FileEnd < Packet
73
+
35
74
  attr_accessor :token, :meta
36
- def initialize(token, meta)
75
+
76
+ def initialize(token, meta, size=nil)
37
77
  @token = token
38
78
  @meta = meta
79
+ @size = size
39
80
  end
40
81
 
41
82
  def self.json_create(o)
42
83
  i = o['data']
43
- new(i['token'], i['meta'])
84
+ new(i['token'], i['meta'], o['size'])
85
+ end
86
+
87
+ def to_s
88
+ wrap("#{super} <#{token}> meta #{meta}")
44
89
  end
45
90
  end
46
91
 
47
92
  # packet that carries data chunks during a file transfer
48
93
  class FileChunk < Packet
94
+
49
95
  attr_accessor :chunk, :token
50
- def initialize(token, chunk=nil)
96
+
97
+ def initialize(token, size=nil, chunk=nil)
51
98
  @chunk = chunk
52
99
  @token = token
100
+ @size = size
53
101
  end
102
+
54
103
  def self.json_create(o)
55
104
  i = o['data']
56
- new(i['token'], i['chunk'])
105
+ new(i['token'], o['size'], i['chunk'])
106
+ end
107
+
108
+ def to_s
109
+ "#{super} <#{token}>"
57
110
  end
58
111
  end
59
112
 
@@ -71,25 +124,43 @@ module Nanite
71
124
  # target is the target nanite for the request
72
125
  # persistent signifies if this request should be saved to persistent storage by the AMQP broker
73
126
  class Request < Packet
127
+
74
128
  attr_accessor :from, :payload, :type, :token, :reply_to, :selector, :target, :persistent, :tags
129
+
75
130
  DEFAULT_OPTIONS = {:selector => :least_loaded}
76
- def initialize(type, payload, opts={})
131
+
132
+ def initialize(type, payload, size=nil, opts={})
77
133
  opts = DEFAULT_OPTIONS.merge(opts)
78
- @type = type
79
- @payload = payload
80
- @from = opts[:from]
81
- @token = opts[:token]
82
- @reply_to = opts[:reply_to]
83
- @selector = opts[:selector]
84
- @target = opts[:target]
85
- @persistent = opts[:persistent]
86
- @tags = opts[:tags] || []
134
+ @type = type
135
+ @payload = payload
136
+ @size = size
137
+ @from = opts[:from]
138
+ @token = opts[:token]
139
+ @reply_to = opts[:reply_to]
140
+ @selector = opts[:selector]
141
+ @target = opts[:target]
142
+ @persistent = opts[:persistent]
143
+ @tags = opts[:tags] || []
87
144
  end
145
+
88
146
  def self.json_create(o)
89
147
  i = o['data']
90
- new(i['type'], i['payload'], {:from => i['from'], :token => i['token'], :reply_to => i['reply_to'], :selector => i['selector'],
91
- :target => i['target'], :persistent => i['persistent'], :tags => i['tags']})
148
+ new(i['type'], i['payload'], o['size'], { :from => i['from'], :token => i['token'],
149
+ :reply_to => i['reply_to'], :selector => i['selector'],
150
+ :target => i['target'], :persistent => i['persistent'],
151
+ :tags => i['tags'] })
152
+ end
153
+
154
+ def to_s(filter=nil)
155
+ log_msg = "#{super} <#{token}> #{type}"
156
+ log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
157
+ log_msg += " to #{id_to_s(target)}" if target && (filter.nil? || filter.include?(:target))
158
+ log_msg += ", reply_to #{id_to_s(reply_to)}" if reply_to && (filter.nil? || filter.include?(:reply_to))
159
+ log_msg += ", tags #{tags.inspect}" if tags && !tags.empty? && (filter.nil? || filter.include?(:tags))
160
+ log_msg += ", payload #{payload.inspect}" if filter.nil? || filter.include?(:payload)
161
+ wrap(log_msg)
92
162
  end
163
+
93
164
  end
94
165
 
95
166
  # packet that means a work push from mapper
@@ -105,23 +176,38 @@ module Nanite
105
176
  # target is the target nanite for the request
106
177
  # persistent signifies if this request should be saved to persistent storage by the AMQP broker
107
178
  class Push < Packet
179
+
108
180
  attr_accessor :from, :payload, :type, :token, :selector, :target, :persistent, :tags
181
+
109
182
  DEFAULT_OPTIONS = {:selector => :least_loaded}
110
- def initialize(type, payload, opts={})
183
+
184
+ def initialize(type, payload, size=nil, opts={})
111
185
  opts = DEFAULT_OPTIONS.merge(opts)
112
- @type = type
113
- @payload = payload
114
- @from = opts[:from]
115
- @token = opts[:token]
116
- @selector = opts[:selector]
117
- @target = opts[:target]
118
- @persistent = opts[:persistent]
119
- @tags = opts[:tags] || []
186
+ @type = type
187
+ @payload = payload
188
+ @size = size
189
+ @from = opts[:from]
190
+ @token = opts[:token]
191
+ @selector = opts[:selector]
192
+ @target = opts[:target]
193
+ @persistent = opts[:persistent]
194
+ @tags = opts[:tags] || []
120
195
  end
196
+
121
197
  def self.json_create(o)
122
198
  i = o['data']
123
- new(i['type'], i['payload'], {:from => i['from'], :token => i['token'], :selector => i['selector'],
124
- :target => i['target'], :persistent => i['persistent'], :tags => i['tags']})
199
+ new(i['type'], i['payload'], o['size'], { :from => i['from'], :token => i['token'],
200
+ :selector => i['selector'], :target => i['target'],
201
+ :persistent => i['persistent'], :tags => i['tags'] })
202
+ end
203
+
204
+ def to_s(filter=nil)
205
+ log_msg = "#{super} <#{token}> #{type}"
206
+ log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
207
+ log_msg += ", target #{id_to_s(target)}" if target && (filter.nil? || filter.include?(:target))
208
+ log_msg += ", tags #{tags.inspect}" if tags && !tags.empty? && (filter.nil? || filter.include?(:tags))
209
+ log_msg += ", payload #{payload.inspect}" if filter.nil? || filter.include?(:payload)
210
+ wrap(log_msg)
125
211
  end
126
212
  end
127
213
 
@@ -132,16 +218,28 @@ module Nanite
132
218
  # token is a generated request id that mapper uses to identify replies
133
219
  # to is identity of the node result should be delivered to
134
220
  class Result < Packet
221
+
135
222
  attr_accessor :token, :results, :to, :from
136
- def initialize(token, to, results, from)
223
+
224
+ def initialize(token, to, results, from, size=nil)
137
225
  @token = token
138
226
  @to = to
139
227
  @from = from
140
228
  @results = results
229
+ @size = size
141
230
  end
231
+
142
232
  def self.json_create(o)
143
233
  i = o['data']
144
- new(i['token'], i['to'], i['results'], i['from'])
234
+ new(i['token'], i['to'], i['results'], i['from'], o['size'])
235
+ end
236
+
237
+ def to_s(filter=nil)
238
+ log_msg = "#{super} <#{token}>"
239
+ log_msg += " from #{id_to_s(from)}" if filter.nil? || filter.include?(:from)
240
+ log_msg += " to #{id_to_s(to)}" if filter.nil? || filter.include?(:to)
241
+ log_msg += " results: #{results.inspect}" if filter.nil? || filter.include?(:results)
242
+ wrap(log_msg)
145
243
  end
146
244
  end
147
245
 
@@ -153,17 +251,25 @@ module Nanite
153
251
  # token is a generated request id that mapper uses to identify replies
154
252
  # to is identity of the node result should be delivered to
155
253
  class IntermediateMessage < Packet
254
+
156
255
  attr_accessor :token, :messagekey, :message, :to, :from
157
- def initialize(token, to, from, messagekey, message)
158
- @token = token
159
- @to = to
160
- @from = from
256
+
257
+ def initialize(token, to, from, messagekey, message, size=nil)
258
+ @token = token
259
+ @to = to
260
+ @from = from
161
261
  @messagekey = messagekey
162
- @message = message
262
+ @message = message
263
+ @size = size
163
264
  end
265
+
164
266
  def self.json_create(o)
165
267
  i = o['data']
166
- new(i['token'], i['to'], i['from'], i['messagekey'], i['message'])
268
+ new(i['token'], i['to'], i['from'], i['messagekey'], i['message'], o['size'])
269
+ end
270
+
271
+ def to_s
272
+ wrap("#{super} <#{token}> from #{id_to_s(from)}, key #{messagekey}")
167
273
  end
168
274
  end
169
275
 
@@ -174,16 +280,27 @@ module Nanite
174
280
  # status is a load of the node by default, but may be any criteria
175
281
  # agent may use to report it's availability, load, etc
176
282
  class Register < Packet
283
+
177
284
  attr_accessor :identity, :services, :status, :tags
178
- def initialize(identity, services, status, tags)
179
- @status = status
180
- @tags = tags
285
+
286
+ def initialize(identity, services, status, tags, size=nil)
287
+ @status = status
288
+ @tags = tags
181
289
  @identity = identity
182
290
  @services = services
291
+ @size = size
183
292
  end
293
+
184
294
  def self.json_create(o)
185
295
  i = o['data']
186
- new(i['identity'], i['services'], i['status'], i['tags'])
296
+ new(i['identity'], i['services'], i['status'], i['tags'], o['size'])
297
+ end
298
+
299
+ def to_s
300
+ log_msg = "#{super} #{id_to_s(identity)}"
301
+ log_msg += ", services: #{services.join(', ')}" if services && !services.empty?
302
+ log_msg += ", tags: #{tags.join(', ')}" if tags && !tags.empty?
303
+ wrap(log_msg)
187
304
  end
188
305
  end
189
306
 
@@ -191,13 +308,21 @@ module Nanite
191
308
  #
192
309
  # from is sender identity
193
310
  class UnRegister < Packet
311
+
194
312
  attr_accessor :identity
195
- def initialize(identity)
313
+
314
+ def initialize(identity, size=nil)
196
315
  @identity = identity
316
+ @size = size
197
317
  end
318
+
198
319
  def self.json_create(o)
199
320
  i = o['data']
200
- new(i['identity'])
321
+ new(i['identity'], o['size'])
322
+ end
323
+
324
+ def to_s
325
+ "#{super} #{id_to_s(identity)}"
201
326
  end
202
327
  end
203
328
 
@@ -206,26 +331,40 @@ module Nanite
206
331
  # identity is sender's identity
207
332
  # status is sender's status (see Register packet documentation)
208
333
  class Ping < Packet
334
+
209
335
  attr_accessor :identity, :status
210
- def initialize(identity, status)
211
- @status = status
336
+
337
+ def initialize(identity, status, size=nil)
338
+ @status = status
212
339
  @identity = identity
340
+ @size = size
213
341
  end
342
+
214
343
  def self.json_create(o)
215
344
  i = o['data']
216
- new(i['identity'], i['status'])
345
+ new(i['identity'], i['status'], o['size'])
346
+ end
347
+
348
+ def to_s
349
+ "#{super} #{id_to_s(identity)} status #{status}"
217
350
  end
351
+
218
352
  end
219
353
 
220
354
  # packet that is sent by workers to the mapper
221
355
  # when worker initially comes online to advertise
222
356
  # it's services
223
357
  class Advertise < Packet
224
- def initialize
358
+
359
+ def initialize(size=nil)
360
+ @size = size
225
361
  end
362
+
226
363
  def self.json_create(o)
227
- new
364
+ new(o['size'])
228
365
  end
366
+
229
367
  end
368
+
230
369
  end
231
370
 
data/lib/nanite/state.rb CHANGED
@@ -30,7 +30,7 @@ module Nanite
30
30
  # of these two service tags
31
31
 
32
32
  def initialize(redis)
33
- Nanite::Log.info("initializing redis state: #{redis}")
33
+ Nanite::Log.info("[setup] initializing redis state: #{redis}")
34
34
  host, port = redis.split(':')
35
35
  host ||= '127.0.0.1'
36
36
  port ||= '6379'
@@ -49,14 +49,14 @@ describe Nanite::ActorRegistry do
49
49
 
50
50
  it "should log info message that actor was registered" do
51
51
  importer = WebDocumentImporter.new
52
- Nanite::Log.should_receive(:info).with("Registering #{importer.inspect} with prefix nil")
52
+ Nanite::Log.should_receive(:info).with("[actor] #{importer.class.to_s}")
53
53
  @registry.register(importer, nil)
54
54
  end
55
55
 
56
56
  it "should handle actors registered with a custom prefix" do
57
57
  importer = WebDocumentImporter.new
58
58
  @registry.register(importer, 'monkey')
59
- @registry.actors['monkey'].should == importer
59
+ @registry.actor_for('monkey').should == importer
60
60
  end
61
61
 
62
62
  end # Nanite::ActorRegistry
data/spec/cluster_spec.rb CHANGED
@@ -2,6 +2,8 @@ require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
3
  describe Nanite::Cluster do
4
4
 
5
+ include SpecHelpers
6
+
5
7
  describe "Intialization" do
6
8
 
7
9
  before(:each) do
@@ -91,6 +93,26 @@ describe Nanite::Cluster do
91
93
 
92
94
  end # Reaper
93
95
 
96
+ describe "State" do
97
+ begin
98
+ require 'nanite/state'
99
+ rescue LoadError
100
+ end
101
+
102
+ if defined?(Redis)
103
+ it "should use a local state by default" do
104
+ cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper)
105
+ cluster.nanites.instance_of?(Nanite::LocalState).should == true
106
+ end
107
+
108
+ it "should set up a redis state when requested" do
109
+ state = Nanite::State.new("")
110
+ Nanite::State.should_receive(:new).with("localhost:1234").and_return(state)
111
+ cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper, "localhost:1234")
112
+ cluster.nanites.instance_of?(Nanite::State).should == true
113
+ end
114
+ end
115
+ end
94
116
  end # Intialization
95
117
 
96
118
 
@@ -189,9 +211,94 @@ describe Nanite::Cluster do
189
211
  @cluster.register(@register_packet)
190
212
  end
191
213
 
214
+ describe "with registered callbacks" do
215
+ before(:each) do
216
+ @register_callback = lambda {|request, mapper|}
217
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper, nil, :register => @register_callback)
218
+ end
219
+
220
+ it "should call the registration callback" do
221
+ @register_callback.should_receive(:call).with("nanite_id", @mapper)
222
+ @cluster.register(@register_packet)
223
+ end
224
+ end
225
+
226
+ describe "when sending an invalid packet to the registration queue" do
227
+ it "should log a message statement" do
228
+ Nanite::Log.logger.should_receive(:warn).with("RECV [register] Invalid packet type: Nanite::Ping")
229
+ @cluster.register(Nanite::Ping.new(nil, nil))
230
+ end
231
+ end
192
232
  end # Nanite Registration
193
233
 
194
-
234
+ describe "Unregister" do
235
+ before(:each) do
236
+ @fanout = mock("fanout")
237
+ @binding = mock("binding", :subscribe => true)
238
+ @queue = mock("queue", :bind => @binding)
239
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
240
+ @serializer = mock("Serializer")
241
+ @reaper = mock("Reaper", :timeout => true)
242
+ Nanite::Log.stub!(:info)
243
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
244
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
245
+ @cluster.nanites["nanite_id"] = "nanite_id"
246
+ @unregister_packet = Nanite::UnRegister.new("nanite_id")
247
+ end
248
+
249
+ it "should delete the nanite" do
250
+ @cluster.register(@unregister_packet)
251
+ @cluster.nanites["nanite_id"].should == nil
252
+ end
253
+
254
+ describe "with registered callbacks" do
255
+ before(:each) do
256
+ @unregister_callback = lambda {|request, mapper| }
257
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper, nil, :unregister => @unregister_callback)
258
+ @cluster.nanites["nanite_id"] = "nanite_id"
259
+ end
260
+
261
+ it "should call the unregister callback" do
262
+ @unregister_callback.should_receive(:call).with("nanite_id", @mapper)
263
+ @cluster.register(@unregister_packet)
264
+ end
265
+ end
266
+ end
267
+
268
+ describe "Nanite timed out" do
269
+ before(:each) do
270
+ @fanout = mock("fanout")
271
+ @binding = mock("binding", :subscribe => true)
272
+ @queue = mock("queue", :bind => @binding)
273
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
274
+ @serializer = mock("Serializer")
275
+ Nanite::Log.stub!(:info)
276
+ @register_packet = Nanite::Register.new("nanite_id", ["the_nanite_services"], "nanite_status",[])
277
+ end
278
+
279
+ it "should remove the nanite when timed out" do
280
+ EM.run do
281
+ @cluster = Nanite::Cluster.new(@amq, 0.01, "the_identity", @serializer, @mapper)
282
+ @cluster.register(@register_packet)
283
+ EM.add_timer(1.1) {
284
+ @cluster.nanites["nanite_id"].should == nil
285
+ EM.stop_event_loop
286
+ }
287
+ end
288
+ end
289
+
290
+ it "should call the timed out callback handler when registered" do
291
+ EM.run do
292
+ @cluster = Nanite::Cluster.new(@amq, 0.01, "the_identity", @serializer, @mapper)
293
+ @cluster.register(@register_packet)
294
+ EM.add_timer(1.1) {
295
+ @cluster.nanites["nanite_id"].should == nil
296
+ EM.stop_event_loop
297
+ }
298
+ end
299
+ end
300
+ end
301
+
195
302
  describe "Route" do
196
303
 
197
304
  before(:each) do
@@ -231,7 +338,7 @@ describe Nanite::Cluster do
231
338
  @reaper = mock("Reaper")
232
339
  Nanite::Reaper.stub!(:new).and_return(@reaper)
233
340
  @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
234
- @request = mock("Request", :persistent => true)
341
+ @request = mock("Request", :persistent => true, :target => nil, :target= => nil, :to_s => nil)
235
342
  @target = mock("Target of Request")
236
343
  end
237
344
 
@@ -274,9 +381,10 @@ describe Nanite::Cluster do
274
381
  @reaper = mock("Reaper")
275
382
  Nanite::Reaper.stub!(:new).and_return(@reaper)
276
383
  @request_without_target = mock("Request", :target => nil, :token => "Token",
277
- :reply_to => "Reply To", :from => "From", :persistent => true, :identity => "Identity")
384
+ :reply_to => "Reply To", :from => "From", :persistent => true, :identity => "Identity",
385
+ :payload => "Payload", :to_s => nil)
278
386
  @request_with_target = mock("Request", :target => "Target", :token => "Token",
279
- :reply_to => "Reply To", :from => "From", :persistent => true)
387
+ :reply_to => "Reply To", :from => "From", :persistent => true, :payload => "Payload", :to_s => nil)
280
388
  @mapper_with_target = mock("Mapper", :identity => "id")
281
389
  @mapper_without_target = mock("Mapper", :request => false, :identity => @request_without_target.identity)
282
390
  @cluster_with_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_with_target)
@@ -292,9 +400,78 @@ describe Nanite::Cluster do
292
400
  it "should reply back with nil results for requests with no target when offline queue is disabled" do
293
401
  @mapper_without_target.should_receive(:send_request).with(@request_without_target, anything())
294
402
  Nanite::Result.should_receive(:new).with(@request_without_target.token, @request_without_target.from, nil, @request_without_target.identity)
295
- @cluster_without_target.__send__(:handle_request, @request_without_target)
403
+ @cluster_without_target.__send__(:handle_request, @request_without_target)
296
404
  end
297
405
 
406
+ it "should hand in an intermediate handler" do
407
+ @mapper_with_target.should_receive(:send_request) do |request, opts|
408
+ opts[:intermediate_handler].should be_instance_of(Proc)
409
+ end
410
+
411
+ @cluster_with_target.__send__(:handle_request, @request_with_target)
412
+ end
413
+
414
+ it "should forward the message when send_request failed" do
415
+ @mapper_with_target.stub!(:send_request).and_return(false)
416
+ @cluster_with_target.should_receive(:forward_response)
417
+ @cluster_with_target.__send__(:handle_request, @request_with_target)
418
+ end
298
419
  end # Agent Request Handling
299
420
 
421
+ describe "Heartbeat" do
422
+ before(:each) do
423
+ @fanout = mock("fanout")
424
+ @binding = mock("binding", :subscribe => true)
425
+ @queue = mock("queue", :bind => @binding, :publish => true)
426
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
427
+ @serializer = mock("Serializer", :dump => "dumped_value")
428
+ Nanite::Log.stub!(:info)
429
+ @ping = stub("ping", :status => 0.3, :identity => "nanite_id")
430
+ end
431
+
432
+ it "should update the nanite status" do
433
+ run_in_em do
434
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
435
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
436
+ @cluster.send :handle_ping, @ping
437
+ @cluster.nanites["nanite_id"][:status].should == 0.3
438
+ end
439
+ end
440
+
441
+ it "should reset the agent time out" do
442
+ run_in_em do
443
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
444
+ @cluster.reaper.should_receive(:reset_with_autoregister_hack).with("nanite_id", 33)
445
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
446
+ @cluster.send :handle_ping, @ping
447
+ end
448
+ end
449
+
450
+ describe "when timing out after a heartbeat" do
451
+ it "should remove the nanite" do
452
+ run_in_em(false) do
453
+ @cluster = Nanite::Cluster.new(@amq, 0.1, "the_identity", @serializer, @mapper)
454
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
455
+ @cluster.send :handle_ping, @ping
456
+ EM.add_timer(1.5) do
457
+ @cluster.nanites["nanite_id"].should == nil
458
+ EM.stop_event_loop
459
+ end
460
+ end
461
+ end
462
+
463
+ it "should call the timeout callback when defined" do
464
+ run_in_em(false) do
465
+ @timeout_callback = lambda {|nanite, mapper| }
466
+ @timeout_callback.should_receive(:call).with("nanite_id", @mapper)
467
+ @cluster = Nanite::Cluster.new(@amq, 0.1, "the_identity", @serializer, @mapper, nil, :timeout => @timeout_callback)
468
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
469
+ @cluster.send :handle_ping, @ping
470
+ EM.add_timer(1.5) do
471
+ EM.stop_event_loop
472
+ end
473
+ end
474
+ end
475
+ end
476
+ end
300
477
  end # Nanite::Cluster
data/spec/job_spec.rb CHANGED
@@ -33,7 +33,44 @@ describe Nanite::JobWarden do
33
33
 
34
34
  end # Creating a new Job
35
35
 
36
-
36
+ describe "Processing an intermediate message" do
37
+ before(:each) do
38
+ @intm_handler = lambda {|arg1, arg2, arg3| puts 'ehlo'}
39
+ @message = mock("Message", :token => "3faba24fcc", :from => 'nanite-agent')
40
+ @serializer = mock("Serializer", :load => @message)
41
+ @warden = Nanite::JobWarden.new(@serializer)
42
+ @job = Nanite::Job.new(stub("request", :token => "3faba24fcc"), [], @intm_handler)
43
+ @job.instance_variable_set(:@pending_keys, ["defaultkey"])
44
+ @job.instance_variable_set(:@intermediate_state, {"nanite-agent" => {"defaultkey" => [1]}})
45
+ @warden.jobs[@job.token] = @job
46
+ Nanite::Log.stub!(:debug)
47
+ Nanite::Log.stub!(:error)
48
+ end
49
+
50
+ it "should call the intermediate handler with three parameters" do
51
+ @intm_handler.should_receive(:call).with("defaultkey", "nanite-agent", 1)
52
+ @warden.process(@message)
53
+ end
54
+
55
+ it "should call the intermediate handler with four parameters" do
56
+ @intm_handler.stub!(:arity).and_return(4)
57
+ @intm_handler.should_receive(:call).with("defaultkey", "nanite-agent", 1, @job)
58
+ @warden.process(@message)
59
+ end
60
+
61
+ it "should not call the intermediate handler when it can't be found for the specified key" do
62
+ @intm_handler.should_not_receive(:call)
63
+ @job.instance_variable_set(:@intermediate_handler, nil)
64
+ @warden.process(@message)
65
+ end
66
+
67
+ it "should call the intermediate handler with one parameter which needs to be the result" do
68
+ @intm_handler.should_receive(:call).with(1, @job)
69
+ @intm_handler.stub!(:arity).and_return(2)
70
+ @warden.process(@message)
71
+ end
72
+ end
73
+
37
74
  describe "Processing a Message" do
38
75
 
39
76
  before(:each) do
@@ -44,11 +81,6 @@ describe Nanite::JobWarden do
44
81
  Nanite::Log.stub!(:debug)
45
82
  end
46
83
 
47
- it "should log debug message about message to be processed" do
48
- Nanite::Log.should_receive(:debug)
49
- @warden.process(@message)
50
- end
51
-
52
84
  it "should hand over processing to job" do
53
85
  Nanite::Job.stub!(:new).and_return(@job)
54
86
  @job.should_receive(:process).with(@message)
data/spec/spec_helper.rb CHANGED
@@ -6,7 +6,10 @@ require 'spec'
6
6
  require 'nanite'
7
7
 
8
8
  module SpecHelpers
9
-
9
+
10
+ # Initialize logger so it writes to file instead of STDOUT
11
+ Nanite::Log.init('test', File.join(File.dirname(__FILE__)))
12
+
10
13
  # Create test certificate
11
14
  def issue_cert
12
15
  test_dn = { 'C' => 'US',
@@ -20,4 +23,11 @@ module SpecHelpers
20
23
  [ Nanite::Certificate.new(key, dn, dn), key ]
21
24
  end
22
25
 
26
+ def run_in_em(stop_event_loop = true)
27
+ EM.run do
28
+ yield
29
+ EM.stop_event_loop if stop_event_loop
30
+ end
31
+ end
32
+
23
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ezmobius-nanite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1.1
4
+ version: 0.4.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -102,6 +102,7 @@ files:
102
102
  - spec/distinguished_name_spec.rb
103
103
  has_rdoc: true
104
104
  homepage: http://github.com/ezmobius/nanite
105
+ licenses:
105
106
  post_install_message:
106
107
  rdoc_options: []
107
108
 
@@ -122,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
123
  requirements: []
123
124
 
124
125
  rubyforge_project:
125
- rubygems_version: 1.2.0
126
+ rubygems_version: 1.3.5
126
127
  signing_key:
127
128
  specification_version: 2
128
129
  summary: self assembling fabric of ruby daemons