vitobotta-resque-pool 0.3.0.dev

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.
@@ -0,0 +1,331 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'resque'
3
+ require 'resque/pool/version'
4
+ require 'resque/pool/logging'
5
+ require 'resque/pool/pooled_worker'
6
+ require 'fcntl'
7
+ require 'yaml'
8
+
9
+ module Resque
10
+ class Pool
11
+ SIG_QUEUE_MAX_SIZE = 5
12
+ DEFAULT_WORKER_INTERVAL = 5
13
+ QUEUE_SIGS = [ :QUIT, :INT, :TERM, :USR1, :USR2, :CONT, :HUP, :WINCH, ]
14
+ CHUNK_SIZE = (16 * 1024)
15
+
16
+ include Logging
17
+ attr_reader :config
18
+ attr_reader :workers
19
+
20
+ def initialize(config)
21
+ init_config(config)
22
+ @workers = {}
23
+ procline "(initialized)"
24
+ end
25
+
26
+ # Config: after_prefork {{{
27
+
28
+ # The `after_prefork` hook will be run in workers if you are using the
29
+ # preforking master worker to save memory. Use this hook to reload
30
+ # database connections and so forth to ensure that they're not shared
31
+ # among workers.
32
+ #
33
+ # Call with a block to set the hook.
34
+ # Call with no arguments to return the hook.
35
+ def self.after_prefork(&block)
36
+ block ? (@after_prefork = block) : @after_prefork
37
+ end
38
+
39
+ # Set the after_prefork proc.
40
+ def self.after_prefork=(after_prefork)
41
+ @after_prefork = after_prefork
42
+ end
43
+
44
+ def call_after_prefork!
45
+ self.class.after_prefork && self.class.after_prefork.call
46
+ end
47
+
48
+ # }}}
49
+ # Config: class methods to start up the pool using the default config {{{
50
+
51
+ @config_files = ["resque-pool.yml", "config/resque-pool.yml"]
52
+ class << self; attr_accessor :config_files; end
53
+ def self.choose_config_file
54
+ if ENV["RESQUE_POOL_CONFIG"]
55
+ ENV["RESQUE_POOL_CONFIG"]
56
+ else
57
+ @config_files.detect { |f| File.exist?(f) }
58
+ end
59
+ end
60
+
61
+ def self.run
62
+ if GC.respond_to?(:copy_on_write_friendly=)
63
+ GC.copy_on_write_friendly = true
64
+ end
65
+ Resque::Pool.new(choose_config_file).start.join
66
+ end
67
+
68
+ # }}}
69
+ # Config: load config and config file {{{
70
+
71
+ def config_file
72
+ @config_file || (!@config && ::Resque::Pool.choose_config_file)
73
+ end
74
+
75
+ def init_config(config)
76
+ case config
77
+ when String, nil
78
+ @config_file = config
79
+ else
80
+ @config = config.dup
81
+ end
82
+ load_config
83
+ end
84
+
85
+ def load_config
86
+ if config_file
87
+ @config = YAML.load_file(config_file)
88
+ else
89
+ @config ||= {}
90
+ end
91
+ environment and @config[environment] and config.merge!(@config[environment])
92
+ config.delete_if {|key, value| value.is_a? Hash }
93
+ end
94
+
95
+ def environment
96
+ if defined? RAILS_ENV
97
+ RAILS_ENV
98
+ else
99
+ ENV['RACK_ENV'] || ENV['RAILS_ENV'] || ENV['RESQUE_ENV']
100
+ end
101
+ end
102
+
103
+ # }}}
104
+
105
+ # Sig handlers and self pipe management {{{
106
+
107
+ def self_pipe; @self_pipe ||= [] end
108
+ def sig_queue; @sig_queue ||= [] end
109
+
110
+ def init_self_pipe!
111
+ self_pipe.each { |io| io.close rescue nil }
112
+ self_pipe.replace(IO.pipe)
113
+ self_pipe.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
114
+ end
115
+
116
+ def init_sig_handlers!
117
+ QUEUE_SIGS.each { |sig| trap_deferred(sig) }
118
+ trap(:CHLD) { |_| awaken_master }
119
+ end
120
+
121
+ def awaken_master
122
+ begin
123
+ self_pipe.last.write_nonblock('.') # wakeup master process from select
124
+ rescue Errno::EAGAIN, Errno::EINTR
125
+ # pipe is full, master should wake up anyways
126
+ retry
127
+ end
128
+ end
129
+
130
+ class QuitNowException < Exception; end
131
+ # defer a signal for later processing in #join (master process)
132
+ def trap_deferred(signal)
133
+ trap(signal) do |sig_nr|
134
+ if @waiting_for_reaper && [:INT, :TERM].include?(signal)
135
+ log "Recieved #{signal}: short circuiting QUIT waitpid"
136
+ raise QuitNowException
137
+ end
138
+ if sig_queue.size < SIG_QUEUE_MAX_SIZE
139
+ sig_queue << signal
140
+ awaken_master
141
+ else
142
+ log "ignoring SIG#{signal}, queue=#{sig_queue.inspect}"
143
+ end
144
+ end
145
+ end
146
+
147
+ def reset_sig_handlers!
148
+ QUEUE_SIGS.each {|sig| trap(sig, "DEFAULT") }
149
+ end
150
+
151
+ def handle_sig_queue!
152
+ case signal = sig_queue.shift
153
+ when :USR1, :USR2, :CONT
154
+ log "#{signal}: sending to all workers"
155
+ signal_all_workers(signal)
156
+ when :HUP
157
+ log "HUP: reload config file and reload logfiles"
158
+ load_config
159
+ Logging.reopen_logs!
160
+ log "HUP: gracefully shutdown old children (which have old logfiles open)"
161
+ signal_all_workers(:QUIT)
162
+ log "HUP: new children will inherit new logfiles"
163
+ maintain_worker_count
164
+ when :WINCH
165
+ log "WINCH: gracefully stopping all workers"
166
+ @config = {}
167
+ maintain_worker_count
168
+ when :QUIT
169
+ log "QUIT: graceful shutdown, waiting for children"
170
+ signal_all_workers(:QUIT)
171
+ reap_all_workers(0) # will hang until all workers are shutdown
172
+ :break
173
+ when :INT
174
+ log "INT: immediate shutdown (graceful worker shutdown)"
175
+ signal_all_workers(:QUIT)
176
+ :break
177
+ when :TERM
178
+ log "TERM: immediate shutdown (and immediate worker shutdown)"
179
+ signal_all_workers(:TERM)
180
+ :break
181
+ end
182
+ end
183
+
184
+ # }}}
185
+ # start, join, and master sleep {{{
186
+
187
+ def start
188
+ procline("(starting)")
189
+ init_self_pipe!
190
+ init_sig_handlers!
191
+ maintain_worker_count
192
+ procline("(started)")
193
+ log "started manager"
194
+ report_worker_pool_pids
195
+ self
196
+ end
197
+
198
+ def report_worker_pool_pids
199
+ if workers.empty?
200
+ log "Pool is empty"
201
+ else
202
+ log "Pool contains worker PIDs: #{all_pids.inspect}"
203
+ end
204
+ end
205
+
206
+ def join
207
+ loop do
208
+ reap_all_workers
209
+ break if handle_sig_queue! == :break
210
+ if sig_queue.empty?
211
+ master_sleep
212
+ maintain_worker_count
213
+ end
214
+ procline("managing #{all_pids.inspect}")
215
+ end
216
+ procline("(shutting down)")
217
+ #stop # gracefully shutdown all workers on our way out
218
+ log "manager finished"
219
+ #unlink_pid_safe(pid) if pid
220
+ end
221
+
222
+ def master_sleep
223
+ begin
224
+ ready = IO.select([self_pipe.first], nil, nil, 1) or return
225
+ ready.first && ready.first.first or return
226
+ loop { self_pipe.first.read_nonblock(CHUNK_SIZE) }
227
+ rescue Errno::EAGAIN, Errno::EINTR
228
+ end
229
+ end
230
+
231
+ # }}}
232
+ # worker process management {{{
233
+
234
+ def reap_all_workers(waitpid_flags=Process::WNOHANG)
235
+ @waiting_for_reaper = waitpid_flags == 0
236
+ begin
237
+ loop do
238
+ # -1, wait for any child process
239
+ wpid, status = Process.waitpid2(-1, waitpid_flags)
240
+ wpid or break
241
+ worker = delete_worker(wpid)
242
+ # TODO: close any file descriptors connected to worker, if any
243
+ log "Reaped resque worker[#{status.pid}] (status: #{status.exitstatus}) queues: #{worker.queues.join(",")}"
244
+ end
245
+ rescue Errno::ECHILD, QuitNowException
246
+ end
247
+ end
248
+
249
+ def delete_worker(pid)
250
+ worker = nil
251
+ workers.detect do |queues, pid_to_worker|
252
+ worker = pid_to_worker.delete(pid)
253
+ end
254
+ worker
255
+ end
256
+
257
+ def all_pids
258
+ workers.map {|q,workers| workers.keys }.flatten
259
+ end
260
+
261
+ def signal_all_workers(signal)
262
+ all_pids.each do |pid|
263
+ Process.kill signal, pid
264
+ end
265
+ end
266
+
267
+ # }}}
268
+ # ???: maintain_worker_count, all_known_queues {{{
269
+
270
+ def maintain_worker_count
271
+ all_known_queues.each do |queues|
272
+ delta = worker_delta_for(queues)
273
+ spawn_missing_workers_for(queues) if delta > 0
274
+ quit_excess_workers_for(queues) if delta < 0
275
+ end
276
+ end
277
+
278
+ def all_known_queues
279
+ config.keys | workers.keys
280
+ end
281
+
282
+ # }}}
283
+ # methods that operate on a single grouping of queues {{{
284
+ # perhaps this means a class is waiting to be extracted
285
+
286
+ def spawn_missing_workers_for(queues)
287
+ worker_delta_for(queues).times do |nr|
288
+ spawn_worker!(queues)
289
+ end
290
+ end
291
+
292
+ def quit_excess_workers_for(queues)
293
+ delta = -worker_delta_for(queues)
294
+ pids_for(queues)[0...delta].each do |pid|
295
+ Process.kill("QUIT", pid)
296
+ end
297
+ end
298
+
299
+ def worker_delta_for(queues)
300
+ config.fetch(queues, 0) - workers.fetch(queues, []).size
301
+ end
302
+
303
+ def pids_for(queues)
304
+ workers[queues].keys
305
+ end
306
+
307
+ def spawn_worker!(queues)
308
+ worker = create_worker(queues)
309
+ pid = fork do
310
+ log_worker "Starting worker #{worker}"
311
+ call_after_prefork!
312
+ reset_sig_handlers!
313
+ #self_pipe.each {|io| io.close }
314
+ worker.work(ENV['INTERVAL'] || DEFAULT_WORKER_INTERVAL) # interval, will block
315
+ end
316
+ workers[queues] ||= {}
317
+ workers[queues][pid] = worker
318
+ end
319
+
320
+ def create_worker(queues)
321
+ queues = queues.to_s.split(',')
322
+ worker = PooledWorker.new(*queues)
323
+ worker.verbose = ENV['LOGGING'] || ENV['VERBOSE']
324
+ worker.very_verbose = ENV['VVERBOSE']
325
+ worker
326
+ end
327
+
328
+ # }}}
329
+
330
+ end
331
+ end
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "resque/pool/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "vitobotta-resque-pool"
7
+ s.version = Resque::Pool::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["nicholas a. evans", "Vito Botta"]
10
+ s.email = ["nick@ekenosen.net", "vito@botta.name"]
11
+ s.homepage = "https://github.com/vitobotta/resque-pool"
12
+ s.summary = "quickly and easily fork a pool of resque workers"
13
+ s.description = <<-EOF
14
+ quickly and easily fork a pool of resque workers,
15
+ saving memory (w/REE) and monitoring their uptime
16
+ EOF
17
+
18
+ s.add_dependency "resque", "~> 1.13"
19
+ s.add_dependency "trollop", "~> 1.16"
20
+ s.add_dependency "rake"
21
+ s.add_development_dependency "rspec", "~> 2.3.0"
22
+ s.add_development_dependency "cucumber", "~> 0.10.0"
23
+ s.add_development_dependency "aruba", "~> 0.3.2"
24
+ s.add_development_dependency "SystemTimer" unless RUBY_VERSION =~ /1\.9/ # to silence redis gem's warning
25
+ s.add_development_dependency "bundler", "~> 1.0"
26
+
27
+ # hidden files are automatically ignored by Dir.glob
28
+ ignore_patterns = %w[**/*.gem **/*.pid **/*.log pkg Gemfile.lock]
29
+ ignore_files = ignore_patterns.inject([]) {|a,p| a + Dir[p] }
30
+ s.files = Dir["**/*"] - ignore_files
31
+ s.test_files = Dir.glob("{spec,features}/**/*.{rb,yml,feature}")
32
+ s.executables = 'resque-pool'
33
+ s.require_paths = ["lib"]
34
+ end
@@ -0,0 +1,6 @@
1
+ RESQUE_POOL_CONFIG = {
2
+ 'foo' => 1,
3
+ 'bar' => 1,
4
+ 'baz' => 1,
5
+ 'foo,bar,baz' => 4,
6
+ }
@@ -0,0 +1,13 @@
1
+ foo: 1
2
+
3
+ production:
4
+ "foo,bar": 10
5
+
6
+ development:
7
+ "foo,bar": 4
8
+ "baz": 23
9
+
10
+ test:
11
+ "bar": 5
12
+ "foo,bar": 3
13
+
@@ -0,0 +1,134 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.configure do |config|
4
+ config.after {
5
+ Object.send(:remove_const, :RAILS_ENV) if defined? RAILS_ENV
6
+ ENV.delete 'RACK_ENV'
7
+ ENV.delete 'RAILS_ENV'
8
+ ENV.delete 'RESQUE_ENV'
9
+ }
10
+ end
11
+
12
+ describe Resque::Pool, "when loading a simple pool configuration" do
13
+ let(:config) do
14
+ { 'foo' => 1, 'bar' => 2, 'foo,bar' => 3, 'bar,foo' => 4, }
15
+ end
16
+ subject { Resque::Pool.new(config) }
17
+
18
+ context "when ENV['RACK_ENV'] is set" do
19
+ before { ENV['RACK_ENV'] = 'development' }
20
+
21
+ it "should load the values from the Hash" do
22
+ subject.config["foo"].should == 1
23
+ subject.config["bar"].should == 2
24
+ subject.config["foo,bar"].should == 3
25
+ subject.config["bar,foo"].should == 4
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ describe Resque::Pool, "when loading the pool configuration from a Hash" do
32
+
33
+ let(:config) do
34
+ {
35
+ 'foo' => 8,
36
+ 'test' => { 'bar' => 10, 'foo,bar' => 12 },
37
+ 'development' => { 'baz' => 14, 'foo,bar' => 16 },
38
+ }
39
+ end
40
+
41
+ subject { Resque::Pool.new(config) }
42
+
43
+ context "when RAILS_ENV is set" do
44
+ before { RAILS_ENV = "test" }
45
+
46
+ it "should load the default values from the Hash" do
47
+ subject.config["foo"].should == 8
48
+ end
49
+
50
+ it "should merge the values for the correct RAILS_ENV" do
51
+ subject.config["bar"].should == 10
52
+ subject.config["foo,bar"].should == 12
53
+ end
54
+
55
+ it "should not load the values for the other environments" do
56
+ subject.config["foo,bar"].should == 12
57
+ subject.config["baz"].should be_nil
58
+ end
59
+
60
+ end
61
+
62
+ context "when ENV['RESQUE_ENV'] is set" do
63
+ before { ENV['RESQUE_ENV'] = 'development' }
64
+ it "should load the config for that environment" do
65
+ subject.config["foo"].should == 8
66
+ subject.config["foo,bar"].should == 16
67
+ subject.config["baz"].should == 14
68
+ subject.config["bar"].should be_nil
69
+ end
70
+ end
71
+
72
+ context "when there is no environment" do
73
+ it "should load the default values only" do
74
+ subject.config["foo"].should == 8
75
+ subject.config["bar"].should be_nil
76
+ subject.config["foo,bar"].should be_nil
77
+ subject.config["baz"].should be_nil
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ describe Resque::Pool, "given no configuration" do
84
+ subject { Resque::Pool.new(nil) }
85
+ it "should have no worker types" do
86
+ subject.config.should == {}
87
+ end
88
+ end
89
+
90
+ describe Resque::Pool, "when loading the pool configuration from a file" do
91
+
92
+ subject { Resque::Pool.new("spec/resque-pool.yml") }
93
+
94
+ context "when RAILS_ENV is set" do
95
+ before { RAILS_ENV = "test" }
96
+
97
+ it "should load the default YAML" do
98
+ subject.config["foo"].should == 1
99
+ end
100
+
101
+ it "should merge the YAML for the correct RAILS_ENV" do
102
+ subject.config["bar"].should == 5
103
+ subject.config["foo,bar"].should == 3
104
+ end
105
+
106
+ it "should not load the YAML for the other environments" do
107
+ subject.config["foo"].should == 1
108
+ subject.config["bar"].should == 5
109
+ subject.config["foo,bar"].should == 3
110
+ subject.config["baz"].should be_nil
111
+ end
112
+
113
+ end
114
+
115
+ context "when ENV['RACK_ENV'] is set" do
116
+ before { ENV['RACK_ENV'] = 'development' }
117
+ it "should load the config for that environment" do
118
+ subject.config["foo"].should == 1
119
+ subject.config["foo,bar"].should == 4
120
+ subject.config["baz"].should == 23
121
+ subject.config["bar"].should be_nil
122
+ end
123
+ end
124
+
125
+ context "when there is no environment" do
126
+ it "should load the default values only" do
127
+ subject.config["foo"].should == 1
128
+ subject.config["bar"].should be_nil
129
+ subject.config["foo,bar"].should be_nil
130
+ subject.config["baz"].should be_nil
131
+ end
132
+ end
133
+
134
+ end
@@ -0,0 +1,3 @@
1
+ require 'rspec'
2
+ $LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
3
+ require 'resque/pool'
@@ -0,0 +1 @@
1
+ require 'resque/pool/tasks'
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vitobotta-resque-pool
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: 6
5
+ version: 0.3.0.dev
6
+ platform: ruby
7
+ authors:
8
+ - nicholas a. evans
9
+ - Vito Botta
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-07-08 00:00:00 +01:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: resque
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ~>
24
+ - !ruby/object:Gem::Version
25
+ version: "1.13"
26
+ type: :runtime
27
+ version_requirements: *id001
28
+ - !ruby/object:Gem::Dependency
29
+ name: trollop
30
+ prerelease: false
31
+ requirement: &id002 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ~>
35
+ - !ruby/object:Gem::Version
36
+ version: "1.16"
37
+ type: :runtime
38
+ version_requirements: *id002
39
+ - !ruby/object:Gem::Dependency
40
+ name: rake
41
+ prerelease: false
42
+ requirement: &id003 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id003
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ prerelease: false
53
+ requirement: &id004 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ version: 2.3.0
59
+ type: :development
60
+ version_requirements: *id004
61
+ - !ruby/object:Gem::Dependency
62
+ name: cucumber
63
+ prerelease: false
64
+ requirement: &id005 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.10.0
70
+ type: :development
71
+ version_requirements: *id005
72
+ - !ruby/object:Gem::Dependency
73
+ name: aruba
74
+ prerelease: false
75
+ requirement: &id006 !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ~>
79
+ - !ruby/object:Gem::Version
80
+ version: 0.3.2
81
+ type: :development
82
+ version_requirements: *id006
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ prerelease: false
86
+ requirement: &id007 !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ~>
90
+ - !ruby/object:Gem::Version
91
+ version: "1.0"
92
+ type: :development
93
+ version_requirements: *id007
94
+ description: " quickly and easily fork a pool of resque workers,\n saving memory (w/REE) and monitoring their uptime\n"
95
+ email:
96
+ - nick@ekenosen.net
97
+ - vito@botta.name
98
+ executables:
99
+ - resque-pool
100
+ extensions: []
101
+
102
+ extra_rdoc_files: []
103
+
104
+ files:
105
+ - bin/resque-pool
106
+ - Changelog.md
107
+ - config/alternate.yml
108
+ - config/cucumber.yml
109
+ - examples/chef_cookbook/recipes/default.rb
110
+ - examples/chef_cookbook/templates/default/initd.erb
111
+ - examples/chef_cookbook/templates/default/monitrc.erb
112
+ - examples/Gemfile
113
+ - examples/Gemfile.lock
114
+ - examples/rails-resque.rake
115
+ - examples/Rakefile
116
+ - examples/resque-pool.yml
117
+ - features/basic_daemon_config.feature
118
+ - features/step_definitions/daemon_steps.rb
119
+ - features/step_definitions/resque-pool_steps.rb
120
+ - features/support/aruba_daemon_support.rb
121
+ - features/support/env.rb
122
+ - Gemfile
123
+ - lib/resque/pool/cli.rb
124
+ - lib/resque/pool/logging.rb
125
+ - lib/resque/pool/pooled_worker.rb
126
+ - lib/resque/pool/tasks.rb
127
+ - lib/resque/pool/version.rb
128
+ - lib/resque/pool.rb
129
+ - LICENSE.txt
130
+ - Rakefile
131
+ - README.md
132
+ - resque-pool.gemspec
133
+ - spec/mock_config.rb
134
+ - spec/resque-pool.yml
135
+ - spec/resque_pool_spec.rb
136
+ - spec/spec_helper.rb
137
+ - tmp/aruba/Rakefile
138
+ has_rdoc: true
139
+ homepage: https://github.com/vitobotta/resque-pool
140
+ licenses: []
141
+
142
+ post_install_message:
143
+ rdoc_options: []
144
+
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: "0"
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ">"
157
+ - !ruby/object:Gem::Version
158
+ version: 1.3.1
159
+ requirements: []
160
+
161
+ rubyforge_project:
162
+ rubygems_version: 1.6.2
163
+ signing_key:
164
+ specification_version: 3
165
+ summary: quickly and easily fork a pool of resque workers
166
+ test_files:
167
+ - spec/mock_config.rb
168
+ - spec/resque_pool_spec.rb
169
+ - spec/spec_helper.rb
170
+ - spec/resque-pool.yml
171
+ - features/step_definitions/daemon_steps.rb
172
+ - features/step_definitions/resque-pool_steps.rb
173
+ - features/support/aruba_daemon_support.rb
174
+ - features/support/env.rb
175
+ - features/basic_daemon_config.feature