woodhouse 0.1.2 → 0.1.5

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.
@@ -54,12 +54,21 @@ but also supplies additional functionality.
54
54
  The dispatcher used for sending out jobs can be set in the Woodhouse config block:
55
55
 
56
56
  Woodhouse.configure do |woodhouse|
57
- woodhouse.dispatcher_type = :local # :local_pool | :amqp
57
+ woodhouse.dispatcher_type = :local # :local_pool | :amqp | :test
58
58
  end
59
59
 
60
60
  Calling the `async` version of a job method sends it to the currently configured dispatcher. The default dispatcher
61
61
  type is `:local`, which simply executes the job synchronously (although still passing it through middleware; see below).
62
62
 
63
+ If you are running tests and you want to be able to test that your code is dispatching Woodhouse jobs (without running
64
+ them), use the `:test` dispatcher and the dispatcher will simply accumulate jobs (of class Woodhouse::Job):
65
+
66
+ IsisWorker.async_pam_gossip :who => "Cyril"
67
+ that_job = Woodhouse.dispatcher.jobs.last
68
+ that_job.worker_class_name # ==> "IsisWorker"
69
+ that_job.job_method # ==> "pam_gossip"
70
+ that_job.arguments[:who] # ==> "Cyril"
71
+
63
72
  If you want `girl_friday` style in-process threaded backgrounding, you can get that by selecting the `:local_pool`
64
73
  dispatcher.
65
74
 
@@ -114,22 +123,40 @@ I've gotten good results from enabling aggressive heap tuning:
114
123
  * Configurable worker sets per server
115
124
  * Configurable number of threads per worker
116
125
  * Segmenting a single queue among multiple workers based on job characteristics (using AMQP header exchanges)
117
- * Progress reporting on jobs
118
- * New Relic background job reporting
126
+ * Extension system
127
+ * Progress reporting on jobs with the `progress` extension
128
+ * New Relic background job reporting with the `new_relic` extension
129
+ * Live status reporting with the `status` extension
119
130
  * Job dispatch and execution middleware stacks
120
131
 
132
+ ## Available Extensions
133
+
134
+ Extensions are loaded in the `Woodhouse.configure` block. Some extensions take arguments.
135
+
136
+ Woodhouse.configure do |woodhouse|
137
+ woodhouse.extension :new_relic
138
+ woodhouse.extension :status, host: "127.0.0.1", port: "10786"
139
+ end
140
+
141
+ ### Built-In
142
+
143
+ * *progress*: Live status reporting on the progress of jobs.
144
+ * *new_relic*: New Relic background job monitoring.
145
+
146
+ ### Packaged Separately
147
+
148
+ * [*status*][https://github.com/mboeh/woodhouse-status]: HTTP server embedded in Woodhouse to provide current status and liveness information via JSON.
149
+
121
150
  ## Upcoming
122
151
 
123
152
  * Live reconfiguration of workers -- add or remove workers across one or more nodes without restarting
124
153
  * Persistent configuration changes -- configuration changes saved to a data store and kept across deploys
125
- * Watchdog/status workers on every node
126
154
  * Web interface
127
155
 
128
156
  ## To Do
129
157
 
130
158
  * Examples and guides
131
159
  * More documentation
132
- * Watchdog system
133
160
 
134
161
  ## Supported Versions
135
162
 
@@ -58,12 +58,20 @@ module Woodhouse
58
58
  RUBY_VERSION.to_f >= 1.9 or %w[jruby rbx].include?(RUBY_ENGINE)
59
59
  end
60
60
 
61
+ def dispatcher
62
+ global_configuration.dispatcher
63
+ end
64
+
61
65
  def dispatch(*a)
62
- global_configuration.dispatcher.dispatch(*a)
66
+ dispatcher.dispatch(*a)
63
67
  end
64
68
 
65
69
  def update_job(*a)
66
- global_configuration.dispatcher.update_job(*a)
70
+ dispatcher.update_job(*a)
71
+ end
72
+
73
+ def watchdog
74
+ Woodhouse::Watchdog.instance
67
75
  end
68
76
 
69
77
  end
@@ -92,6 +100,7 @@ require 'woodhouse/rails'
92
100
  require 'woodhouse/process'
93
101
  require 'woodhouse/layout_serializer'
94
102
  require 'woodhouse/trigger_set'
103
+ require 'woodhouse/watchdog'
95
104
 
96
105
  require 'woodhouse/extension'
97
106
  require 'woodhouse/extensions/progress'
@@ -22,7 +22,7 @@ class Woodhouse::Dispatcher
22
22
 
23
23
  private
24
24
 
25
- def after_initialize
25
+ def after_initialize(config, opts = {}, &blk)
26
26
 
27
27
  end
28
28
 
@@ -15,5 +15,6 @@ require 'woodhouse/dispatchers/local_dispatcher'
15
15
  require 'woodhouse/dispatchers/bunny_dispatcher'
16
16
  require 'woodhouse/dispatchers/hot_bunnies_dispatcher'
17
17
  require 'woodhouse/dispatchers/local_pool_dispatcher'
18
+ require 'woodhouse/dispatchers/test_dispatcher'
18
19
 
19
20
  Woodhouse::Dispatchers::AmqpDispatcher = Woodhouse::Dispatchers.default_amqp_dispatcher
@@ -4,7 +4,7 @@ require 'woodhouse/dispatchers/common_amqp_dispatcher'
4
4
 
5
5
  class Woodhouse::Dispatchers::BunnyDispatcher < Woodhouse::Dispatchers::CommonAmqpDispatcher
6
6
 
7
- def initialize(config)
7
+ def initialize(config, opts = {}, &blk)
8
8
  super
9
9
  @pool = new_pool
10
10
  end
@@ -21,7 +21,7 @@ class Woodhouse::Dispatchers::HotBunniesDispatcher < Woodhouse::Dispatchers::Com
21
21
  raise err
22
22
  }
23
23
  else
24
- def initialize(config)
24
+ def initialize(config, opts = {}, &blk)
25
25
  super
26
26
  new_connection
27
27
  @mutex = Mutex.new
@@ -0,0 +1,31 @@
1
+ # A dispatcher which simply swallows and stores jobs without performing them. This
2
+ # is to be used in testing other applications' interactions with Woodhouse.
3
+ class Woodhouse::Dispatchers::TestDispatcher < Woodhouse::Dispatcher
4
+
5
+ # All jobs (Woodhouse::Job) which have been dispatched since this dispatcher was last cleared.
6
+ attr_reader :jobs
7
+ # All job updates (used in the Progress extension) which have been dispatched since this dispatcher was last cleared.
8
+ attr_reader :job_updates
9
+
10
+ # Wipe out all stored jobs and job updates.
11
+ def clear!
12
+ jobs.clear
13
+ job_updates.clear
14
+ end
15
+
16
+ private
17
+
18
+ def after_initialize(*)
19
+ @jobs = []
20
+ @job_updates = []
21
+ end
22
+
23
+ def deliver_job(job)
24
+ @jobs << job
25
+ end
26
+
27
+ def deliver_job_update(job, data)
28
+ @job_updates << [job, data]
29
+ end
30
+
31
+ end
@@ -14,9 +14,17 @@ class Woodhouse::Extension
14
14
  def install_extension(name, configuration, opts = {}, &blk)
15
15
  if ext = registry[name]
16
16
  ext.install_extension(configuration, opts, &blk)
17
+ else
18
+ ext = load_extension(name)
19
+ ext.install_extension(configuration, opts, &blk)
17
20
  end
18
21
  end
19
22
 
23
+ def load_extension(name)
24
+ require "woodhouse/extensions/#{name}"
25
+ registry[name]
26
+ end
27
+
20
28
  end
21
29
 
22
30
  self.registry = {}
@@ -142,7 +142,9 @@ module Woodhouse::Progress
142
142
 
143
143
  def update_progress(data)
144
144
  job = self
145
- Celluloid::Future.new { progress_sink.update_job(job, data) }
145
+ sink = progress_sink
146
+ Celluloid::InternalPool.get { sink.update_job(job, data) }
147
+ nil
146
148
  end
147
149
 
148
150
  def progress_sink
@@ -149,7 +149,7 @@ module Woodhouse
149
149
  def default_configuration!(config, options = {})
150
150
  options[:threads] ||= config.default_threads
151
151
  config.registry.each do |name, klass|
152
- klass.public_instance_methods(false).each do |method|
152
+ klass.available_jobs.each do |method|
153
153
  add_worker Woodhouse::Layout::Worker.new(name, method, options)
154
154
  end
155
155
  end
@@ -188,16 +188,16 @@ module Woodhouse
188
188
  #
189
189
  class Worker
190
190
  attr_reader :worker_class_name, :job_method, :threads, :criteria
191
+ attr_accessor :flags
191
192
 
192
193
  def initialize(worker_class_name, job_method, opts = {})
193
194
  opts = opts.clone
194
195
  self.worker_class_name = worker_class_name
195
196
  self.job_method = job_method
196
- self.threads = opts.delete(:threads) || 1
197
- self.criteria = opts.delete(:only)
198
- unless opts.keys.empty?
199
- raise ArgumentError, "unknown option keys: #{opts.keys.inspect}"
200
- end
197
+ self.threads = opts.delete(:threads) || 1
198
+ criteria = opts.delete(:only)
199
+ self.flags = opts
200
+ self.criteria = criteria
201
201
  end
202
202
 
203
203
  def exchange_name
@@ -221,7 +221,7 @@ module Woodhouse
221
221
  end
222
222
 
223
223
  def criteria=(value)
224
- @criteria = Woodhouse::QueueCriteria.new(value).freeze
224
+ @criteria = Woodhouse::QueueCriteria.new(value, flags).freeze
225
225
  end
226
226
 
227
227
  def frozen_clone
@@ -15,6 +15,11 @@ class Woodhouse::Process
15
15
  Thread.main.raise Interrupt
16
16
  end
17
17
 
18
+ Woodhouse::Watchdog.start
19
+ Woodhouse::Watchdog.listen do |id, transition|
20
+ Woodhouse.global_configuration.logger.info "[##{id}] #{transition}"
21
+ end
22
+
18
23
  begin
19
24
  @server.start!
20
25
  puts "Woodhouse serving as of #{Time.now}. Ctrl-C to stop."
@@ -25,6 +30,7 @@ class Woodhouse::Process
25
30
  @server.wait(:shutdown)
26
31
  ensure
27
32
  @server.terminate
33
+ Woodhouse::Watchdog.stop
28
34
  exit
29
35
  end
30
36
  end
@@ -2,13 +2,16 @@ module Woodhouse
2
2
 
3
3
  class QueueCriteria
4
4
  attr_reader :criteria
5
+ attr_accessor :exclusive
5
6
 
6
- def initialize(opts = {})
7
- if opts.kind_of?(self.class)
8
- opts = opts.criteria
7
+ def initialize(values = {}, flags = nil)
8
+ flags ||= {}
9
+ self.exclusive ||= flags[:exclusive]
10
+ if values.kind_of?(self.class)
11
+ values = values.criteria
9
12
  end
10
- unless opts.nil?
11
- @criteria = stringify_values(opts).freeze
13
+ unless values.nil?
14
+ @criteria = stringify_values(values).freeze
12
15
  end
13
16
  end
14
17
 
@@ -33,11 +36,17 @@ module Woodhouse
33
36
 
34
37
  def matches?(args)
35
38
  return true if @criteria.nil?
39
+ return false if exclusive? and @criteria.length != args.keys.reject{|k| k =~ /^_/ }.length
40
+
36
41
  @criteria.all? do |key, val|
37
42
  args[key] == val
38
43
  end
39
44
  end
40
45
 
46
+ def exclusive?
47
+ !!exclusive
48
+ end
49
+
41
50
  private
42
51
 
43
52
  def stringify_values(hash)
@@ -27,6 +27,7 @@ class Woodhouse::Runner
27
27
  def initialize(worker, config)
28
28
  @worker = worker
29
29
  @config = config
30
+ @status_client = Woodhouse::Watchdog.client
30
31
  @config.logger.debug "Thread for #{@worker.describe} ready and waiting for jobs"
31
32
  end
32
33
 
@@ -37,6 +38,10 @@ class Woodhouse::Runner
37
38
  raise NotImplementedError, "implement #spin_down in a subclass of Woodhouse::Runner"
38
39
  end
39
40
 
41
+ def current_status
42
+ @status
43
+ end
44
+
40
45
  private
41
46
 
42
47
  # Implement this in a subclass. When this message is received by an actor, it should
@@ -53,8 +58,13 @@ class Woodhouse::Runner
53
58
 
54
59
  # Executes a Job. See Woodhouse::JobExecution.
55
60
  def service_job(job) # :doc:
56
- @config.logger.debug "Servicing job for #{@worker.describe}"
61
+ status :servicing
57
62
  Woodhouse::JobExecution.new(@config, job).execute
58
63
  end
59
64
 
65
+ def status(stat, message = nil)
66
+ message ||= @worker.describe
67
+ @status_client.report stat, message
68
+ end
69
+
60
70
  end
@@ -19,6 +19,7 @@ class Woodhouse::Runners::HotBunniesRunner < Woodhouse::Runner
19
19
  end
20
20
 
21
21
  def subscribe
22
+ status :spinning_up
22
23
  client = HotBunnies.connect(@config.server_info)
23
24
  channel = client.create_channel
24
25
  channel.prefetch = 1
@@ -26,7 +27,9 @@ class Woodhouse::Runners::HotBunniesRunner < Woodhouse::Runner
26
27
  exchange = channel.exchange(@worker.exchange_name, :type => :headers)
27
28
  queue.bind(exchange, :arguments => @worker.criteria.amqp_headers)
28
29
  worker = Celluloid.current_actor
30
+ status :subscribed
29
31
  queue.subscribe(:ack => true).each(:blocking => false) do |headers, payload|
32
+ status :receiving
30
33
  begin
31
34
  job = make_job(headers, payload)
32
35
  if can_service_job?(job)
@@ -36,15 +39,13 @@ class Woodhouse::Runners::HotBunniesRunner < Woodhouse::Runner
36
39
  headers.reject
37
40
  end
38
41
  else
39
- @config.logger.error("Cannot service job #{job.describe} in queue for #{@worker.describe}")
42
+ status :rejected
40
43
  headers.reject
41
44
  end
45
+ status :subscribed
42
46
  rescue => err
47
+ status :error
43
48
  begin
44
- @config.logger.error("Error bubbled up out of worker. This shouldn't happen. #{err.message}")
45
- err.backtrace.each do |btr|
46
- @config.logger.error(" #{btr}")
47
- end
48
49
  headers.reject
49
50
  ensure
50
51
  worker.bail_out(err)
@@ -52,6 +53,7 @@ class Woodhouse::Runners::HotBunniesRunner < Woodhouse::Runner
52
53
  end
53
54
  end
54
55
  wait :spin_down
56
+ status :closing
55
57
  ensure
56
58
  client.close
57
59
  end
@@ -61,6 +63,7 @@ class Woodhouse::Runners::HotBunniesRunner < Woodhouse::Runner
61
63
  end
62
64
 
63
65
  def bail_out(err)
66
+ status :bailing_out, "#{err.class}: #{err.message}"
64
67
  raise Woodhouse::BailOut, "#{err.class}: #{err.message}"
65
68
  end
66
69
 
@@ -1,3 +1,3 @@
1
1
  module Woodhouse
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -0,0 +1,138 @@
1
+ class Woodhouse::Watchdog
2
+ include Celluloid
3
+
4
+ def initialize
5
+ @actors = {}
6
+ @listeners = []
7
+ end
8
+
9
+ def report(id, status)
10
+ last_status = @actors[id]
11
+ @actors[id] = status
12
+ notify id, Transition.new(last_status, status)
13
+ end
14
+
15
+ def status_report
16
+ {}.tap do |hash|
17
+ @actors.each do |id, status|
18
+ hash[id.to_s] = status.to_h
19
+ end
20
+ end
21
+ end
22
+
23
+ def listen(listener)
24
+ @listeners << listener
25
+ end
26
+
27
+ private
28
+
29
+ def notify(id, keyw = {})
30
+ @listeners.each do |listen|
31
+ listen.call id, keyw
32
+ end
33
+ end
34
+
35
+ class << self
36
+
37
+ def instance
38
+ Celluloid::Actor[:woodhouse_watchdog]
39
+ end
40
+
41
+ def start
42
+ @supervisor ||= supervise_as :woodhouse_watchdog
43
+ end
44
+
45
+ def stop
46
+ if @supervisor
47
+ supervisor, @supervisor = @supervisor, nil
48
+ supervisor.terminate
49
+ end
50
+ end
51
+
52
+ def client(id = nil)
53
+ Client.new(instance, id)
54
+ end
55
+
56
+ def listen(listener = nil, &blk)
57
+ if instance
58
+ instance.listen listener || blk
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ class Transition
65
+ attr_reader :old, :new
66
+
67
+ def initialize(old, new)
68
+ @old = old
69
+ @new = new
70
+ end
71
+
72
+ def name
73
+ "#{old_name} -> #{new_name}"
74
+ end
75
+
76
+ def old_name
77
+ old && old.name
78
+ end
79
+
80
+ def new_name
81
+ new && new.name
82
+ end
83
+
84
+ def message
85
+ new.message
86
+ end
87
+
88
+ def duration
89
+ old && new.time - old.time
90
+ end
91
+
92
+ def duration_s
93
+ duration && " (#{duration}s)"
94
+ end
95
+
96
+ def to_s
97
+ "{ #{name} } #{message}#{duration_s}"
98
+ end
99
+
100
+ end
101
+
102
+ class Status
103
+ attr_reader :name, :message, :time
104
+
105
+ def initialize(name, message, time = Time.now)
106
+ @name = name.to_sym
107
+ @message = message.dup.freeze
108
+ @time = time.dup.freeze
109
+
110
+ freeze
111
+ end
112
+
113
+ def to_h
114
+ { name: @name, message: @message, time: @time }
115
+ end
116
+
117
+ end
118
+
119
+ class Client
120
+
121
+ def initialize(watchdog, id = nil)
122
+ @watchdog = watchdog
123
+ @id = id || detect_id || Celluloid.uuid
124
+ end
125
+
126
+ def detect_id
127
+ Celluloid.current_actor.object_id
128
+ end
129
+
130
+ def report(name, message)
131
+ if @watchdog
132
+ @watchdog.report @id, Status.new(name, message)
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -68,6 +68,18 @@ module Woodhouse::Worker
68
68
  end
69
69
  end
70
70
 
71
+ def available_jobs
72
+ @available_jobs ||= public_instance_methods(false)
73
+ end
74
+
75
+ def only_jobs(*jobs)
76
+ @available_jobs = jobs
77
+ end
78
+
79
+ def exclude_jobs(*jobs)
80
+ @available_jobs -= jobs
81
+ end
82
+
71
83
  # You can dispatch a job +baz+ on class +FooBar+ by calling FooBar.async_baz.
72
84
  def method_missing(method, *args, &block)
73
85
  if method.to_s =~ /^asynch?_(.*)/
@@ -21,7 +21,7 @@ describe Woodhouse::LayoutBuilder do
21
21
  # Five workers...
22
22
  default.remove :Ray, :foo
23
23
  # Six workers.
24
- default.add :Ray, :bar, :only => { :baz => "bat" }
24
+ default.add :Ray, :bar, :only => { :baz => "bat" }, :exclusive => true
25
25
  end
26
26
  layout.node(:odin) do |odin|
27
27
  # Two workers.
@@ -45,6 +45,7 @@ describe Woodhouse::LayoutBuilder do
45
45
  }
46
46
  ray.should_not be_nil
47
47
  ray.criteria.matches?("baz" => "bat").should be_true
48
+ ray.criteria.should be_exclusive
48
49
  odin = layout.node(:odin)
49
50
  odin.workers.should have(2).workers
50
51
  odin.workers.first.threads.should == 5
@@ -28,9 +28,10 @@ describe Woodhouse::Progress do
28
28
  context "#tick" do
29
29
 
30
30
  it "should send progress updates" do
31
+ pending "fix for async"
31
32
  ticker = job.status_ticker("orz")
32
33
  sink.should_receive(:update_job).with(job, { "orz" => { "status" => "funky", "current" => 1 } })
33
- ticker.tick(:status => "funky").value
34
+ ticker.tick(:status => "funky")
34
35
  end
35
36
 
36
37
  end
@@ -8,4 +8,13 @@ describe Woodhouse::QueueCriteria do
8
8
  criteria = Woodhouse::QueueCriteria.new("abc" => :def, :fed => 1)
9
9
  criteria.criteria.should == { "abc" => "def", "fed" => "1" }
10
10
  end
11
+
12
+ it "should expect all values to be matched" do
13
+ criteria = Woodhouse::QueueCriteria.new(:orz => "*camper*", :spathi => "fwiffo")
14
+ criteria.matches?("orz" => "*camper*").should be_false
15
+ criteria.matches?("orz" => "*camper*", "spathi" => "fwiffo").should be_true
16
+ criteria.matches?("orz" => "*camper*", "spathi" => "fwiffo", "vux" => "QRJ").should be_true
17
+ criteria.exclusive = true
18
+ criteria.matches?("orz" => "*camper*", "spathi" => "fwiffo", "vux" => "QRJ").should be_false
19
+ end
11
20
  end
@@ -5,6 +5,10 @@ class FakeWorker
5
5
  class << self
6
6
  attr_accessor :last_worker
7
7
  attr_accessor :jobs
8
+
9
+ def available_jobs
10
+ [:foo, :bar]
11
+ end
8
12
  end
9
13
 
10
14
  self.jobs ||= []
@@ -0,0 +1,20 @@
1
+ require 'woodhouse'
2
+
3
+ describe Woodhouse::Dispatchers::TestDispatcher do
4
+
5
+ subject { Woodhouse::Dispatchers::TestDispatcher.new(Woodhouse::NodeConfiguration.new) }
6
+
7
+ it "should store jobs" do
8
+ subject.dispatch "PamPoovey", "shock_fights", "game_changer" => "yes"
9
+ subject.dispatch "SterlingArcher", "spy", "on" => "Ramon Limon"
10
+
11
+ subject.jobs.should have(2).items
12
+ subject.jobs.first.worker_class_name.should == "PamPoovey"
13
+ end
14
+
15
+ it "should store job updates" do
16
+ subject.update_job(:eating, "full" => "not yet")
17
+ subject.job_updates.first.should == [ :eating, { "full" => "not yet" } ]
18
+ end
19
+
20
+ end
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.rubyforge_project = "woodhouse"
20
20
 
21
- s.add_dependency 'celluloid'
21
+ s.add_dependency 'celluloid', '~> 0.12.4'
22
22
  s.add_dependency 'bunny', "~> 0.9.0.pre4"
23
23
  s.add_dependency 'connection_pool'
24
24
  s.add_dependency 'json'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: woodhouse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,24 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-05 00:00:00.000000000 Z
12
+ date: 2013-04-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: celluloid
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
21
+ version: 0.12.4
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - ! '>='
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: '0'
29
+ version: 0.12.4
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: bunny
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -190,6 +190,7 @@ files:
190
190
  - lib/woodhouse/dispatchers/hot_bunnies_dispatcher.rb
191
191
  - lib/woodhouse/dispatchers/local_dispatcher.rb
192
192
  - lib/woodhouse/dispatchers/local_pool_dispatcher.rb
193
+ - lib/woodhouse/dispatchers/test_dispatcher.rb
193
194
  - lib/woodhouse/extension.rb
194
195
  - lib/woodhouse/extensions/new_relic.rb
195
196
  - lib/woodhouse/extensions/new_relic/instrumentation_middleware.rb
@@ -221,6 +222,7 @@ files:
221
222
  - lib/woodhouse/server.rb
222
223
  - lib/woodhouse/trigger_set.rb
223
224
  - lib/woodhouse/version.rb
225
+ - lib/woodhouse/watchdog.rb
224
226
  - lib/woodhouse/worker.rb
225
227
  - spec/integration/bunny_worker_process_spec.rb
226
228
  - spec/layout_builder_spec.rb
@@ -233,6 +235,7 @@ files:
233
235
  - spec/scheduler_spec.rb
234
236
  - spec/server_spec.rb
235
237
  - spec/shared_contexts.rb
238
+ - spec/test_dispatcher_spec.rb
236
239
  - spec/worker_spec.rb
237
240
  - woodhouse.gemspec
238
241
  homepage: http://github.com/mboeh/woodhouse
@@ -249,7 +252,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
249
252
  version: '0'
250
253
  segments:
251
254
  - 0
252
- hash: 545045486718124014
255
+ hash: -1311770161800686581
253
256
  required_rubygems_version: !ruby/object:Gem::Requirement
254
257
  none: false
255
258
  requirements:
@@ -258,7 +261,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
258
261
  version: '0'
259
262
  segments:
260
263
  - 0
261
- hash: 545045486718124014
264
+ hash: -1311770161800686581
262
265
  requirements: []
263
266
  rubyforge_project: woodhouse
264
267
  rubygems_version: 1.8.25