quebert 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,7 +1,6 @@
1
1
  source :rubygems
2
2
 
3
3
  gem 'json'
4
- gem 'daemons'
5
4
  gem 'optitron'
6
5
  gem 'beanstalk-client'
7
6
 
data/README.rdoc CHANGED
@@ -7,11 +7,11 @@ A worker queue framework designed around Beanstalk. Features include:
7
7
  = Features
8
8
 
9
9
  * [x] Multiple back-ends (InProcess, Sync, and Beanstalk)
10
- * [x] Rails/ActiveRecord integration similar to async_observer
10
+ * [ ] Rails/ActiveRecord integration similar to async_observer
11
11
  * [ ] Pluggable exception handling (for Hoptoad integration)
12
12
  * [ ] Reliable daemonization with pidfile support
13
13
 
14
- [x] = Completed, [ ] = Not Started
14
+ [x] = Completed, [ ] = Not Started
15
15
 
16
16
  = How to use
17
17
 
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ begin
11
11
  gem.authors = ["Brad Gessler"]
12
12
  gem.add_development_dependency "rspec", ">= 1.2.9"
13
13
  gem.add_dependency "json"
14
- gem.add_dependency "daemons"
14
+ gem.add_dependency "optitron"
15
15
  gem.add_dependency "beanstalk-client"
16
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
17
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.0.1
data/bin/quebert ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require "quebert"
5
+ rescue LoadError
6
+ quebert_path = File.expand_path('../../lib/', __FILE__)
7
+ $:.unshift(quebert_path)
8
+ require 'rubygems'
9
+ require 'bundler/setup'
10
+ require 'quebert'
11
+ end
12
+
13
+ Quebert::CommandLineRunner.dispatch
data/lib/quebert.rb CHANGED
@@ -1,14 +1,12 @@
1
- $:.unshift File.join(File.dirname(__FILE__), 'quebert')
2
-
3
1
  module Quebert
4
- autoload :Configuration, 'quebert/configuration'
5
- autoload :Job, 'quebert/job'
6
- autoload :Consumer, 'quebert/consumer'
7
- autoload :Backend, 'quebert/backend'
8
- autoload :Support, 'quebert/support'
9
- autoload :Worker, 'quebert/worker'
10
- autoload :Daemonizable, 'quebert/daemonizing'
11
- autoload :AsyncSender, 'quebert/async_sender'
2
+ autoload :Configuration, 'quebert/configuration'
3
+ autoload :Job, 'quebert/job'
4
+ autoload :Consumer, 'quebert/consumer'
5
+ autoload :Backend, 'quebert/backend'
6
+ autoload :Support, 'quebert/support'
7
+ autoload :Worker, 'quebert/worker'
8
+ autoload :CommandLineRunner, 'quebert/command_line_runner'
9
+ autoload :AsyncSender, 'quebert/async_sender'
12
10
 
13
11
  class << self
14
12
  def configuration
@@ -18,12 +16,17 @@ module Quebert
18
16
 
19
17
  # Registry for quebert backends
20
18
  def backends
21
- @backends ||= {}
19
+ @backends ||= Support::Registry.new
20
+ end
21
+
22
+ # Make this easier for elsewhere in the app
23
+ def logger
24
+ config.logger
22
25
  end
23
26
  end
24
27
 
25
28
  # Register built-in Quebert backends
26
- Backend.register :beanstalk, Backend::Beanstalk
27
- Backend.register :in_process, Backend::InProcess
28
- Backend.register :sync, Backend::Sync
29
+ Quebert.backends.register :beanstalk, Backend::Beanstalk
30
+ Quebert.backends.register :in_process, Backend::InProcess
31
+ Quebert.backends.register :sync, Backend::Sync
29
32
  end
@@ -3,9 +3,5 @@ module Quebert
3
3
  autoload :InProcess, 'quebert/backend/in_process.rb'
4
4
  autoload :Beanstalk, 'quebert/backend/beanstalk.rb'
5
5
  autoload :Sync, 'quebert/backend/sync.rb'
6
-
7
- def self.register(name, backend)
8
- Quebert.backends[name.to_sym] = backend
9
- end
10
6
  end
11
7
  end
@@ -0,0 +1,40 @@
1
+ require 'optitron'
2
+
3
+ module Quebert
4
+ class CommandLineRunner < Optitron::CLI
5
+
6
+ desc "Starts a quebert worker"
7
+ opt "pid-file", :type => :string
8
+ opt "log-file", :type => :string
9
+ opt "config-file", :type => :string
10
+ opt "chdir", :type => :string
11
+ def worker
12
+ if dir = params['chdir']
13
+ Dir.chdir dir
14
+ end
15
+
16
+ if pid_file = params['pid-file']
17
+ Support::PidFile.new(pid_file).write!
18
+ end
19
+
20
+ if log_path = params['log-file']
21
+ Quebert.config.log_file_path = log_path
22
+ end
23
+
24
+ if config = params['config-file'] || auto_config
25
+ require config
26
+ end
27
+
28
+ Worker.new.start
29
+ end
30
+
31
+ private
32
+ def auto_config
33
+ rails_env_path = './config/environment.rb'
34
+ if File.exists?(rails_env_path)
35
+ Quebert.logger.info "Detected Rails! Setting config-file=#{File.expand_path(rails_env_path)}"
36
+ rails_env_path
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,16 +1,29 @@
1
+ require 'logger'
2
+
1
3
  module Quebert
2
4
  class Configuration
3
- attr_accessor :backend
5
+ attr_accessor :backend, :logger
4
6
 
5
- def self.from_hash(hash)
6
- hash, config = Support.symbolize_keys(hash), new
7
+ def logger
8
+ @logger ||= Logger.new($stdout)
9
+ end
10
+
11
+ def log_file_path=(path)
12
+ self.logger = Logger.new(path)
13
+ end
14
+
15
+ def from_hash(hash)
16
+ hash = Support.symbolize_keys(hash)
7
17
  # Find out backend from the registry and configure
8
18
  if backend = Quebert.backends[hash.delete(:backend).to_sym]
9
19
  # If the backend supports configuration, do it!
10
- p backend
11
- config.backend = backend.respond_to?(:configure) ? backend.configure(Support.symbolize_keys(hash)) : backend.new
20
+ self.backend = backend.respond_to?(:configure) ? backend.configure(Support.symbolize_keys(hash)) : backend.new
12
21
  end
13
- config
22
+ self
23
+ end
24
+
25
+ def self.from_hash(hash)
26
+ new.from_hash(hash) # Config this puppy up from a config hash
14
27
  end
15
28
  end
16
29
  end
@@ -1,5 +1,9 @@
1
1
  module Quebert
2
2
  module Support
3
+
4
+ autoload :PidFile, 'quebert/support/pid_file'
5
+ autoload :Registry, 'quebert/support/registry'
6
+
3
7
  # Borrowed from Rails ActiveSupport
4
8
  def self.constantize(camel_cased_word) #:nodoc:
5
9
  names = camel_cased_word.split('::')
@@ -0,0 +1,72 @@
1
+ module Quebert
2
+ module Support
3
+
4
+ # Deal with all of our pid file stuff
5
+ class PidFile
6
+
7
+ attr_reader :path
8
+
9
+ ProcessRunning = Class.new(RuntimeError)
10
+
11
+ def initialize(path)
12
+ @path = path
13
+ end
14
+
15
+ def write!
16
+ remove_stale and write
17
+ end
18
+
19
+ # Read pids and turn them into ints
20
+ def self.read(file)
21
+ if File.file?(file) && pid = File.read(file)
22
+ pid.to_i
23
+ else
24
+ nil
25
+ end
26
+ end
27
+
28
+ # Tells us if a current process is running
29
+ def self.running?(pid)
30
+ Process.getpgid(pid) != -1
31
+ rescue Errno::EPERM
32
+ true
33
+ rescue Errno::ESRCH
34
+ false
35
+ end
36
+
37
+ private
38
+ # If PID file is stale, remove it.
39
+ def remove_stale
40
+ if exists? && running?
41
+ raise ProcessRunning, "#{path} already exists, seems like it's already running (process ID: #{pid}). " +
42
+ "Stop the process or delete #{path}."
43
+ else
44
+ remove
45
+ end
46
+ end
47
+
48
+ def remove
49
+ File.delete(path) if exists?
50
+ true
51
+ end
52
+
53
+ def write
54
+ File.open(path,"w") { |f| f.write(Process.pid) }
55
+ File.chmod(0644, path)
56
+ end
57
+
58
+ def pid
59
+ self.class.read(path)
60
+ end
61
+
62
+ def running?
63
+ self.class.running?(pid)
64
+ end
65
+
66
+ def exists?
67
+ File.exist?(path)
68
+ end
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,13 @@
1
+ module Quebert
2
+ module Support
3
+ class Registry < Hash
4
+ def register(key, val)
5
+ self[key.to_sym] = val
6
+ end
7
+
8
+ def unregister(key)
9
+ self.delete(key.to_sym)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -2,9 +2,7 @@ require 'logger'
2
2
 
3
3
  module Quebert
4
4
  class Worker
5
- attr_accessor :exception_handler, :log_file, :backend
6
-
7
- include Quebert::Daemonizable
5
+ attr_accessor :exception_handler, :logger, :backend
8
6
 
9
7
  def initialize
10
8
  yield self if block_given?
@@ -12,18 +10,30 @@ module Quebert
12
10
 
13
11
  # Start the worker backend and intercept exceptions if a handler is provided
14
12
  def start
15
- while job = backend.reserve do
13
+ logger.info "Worker pid##{Process.pid} started with #{backend.class.name} backend"
14
+ while consumer = backend.reserve do
16
15
  begin
17
- job.perform
16
+ log consumer.job, "performing with args #{consumer.job.args.inspect}"
17
+ consumer.perform
18
+ log consumer.job, "complete"
18
19
  rescue Exception => e
20
+ log consumer.job, "fault #{e}", :error
19
21
  exception_handler ? exception_handler.call(e) : raise(e)
20
22
  end
21
23
  end
22
24
  end
23
25
 
24
26
  protected
25
- def log(message)
26
- puts message
27
+ def log(job, message, level=:info)
28
+ logger.send(level, "#{job.class.name}##{job.object_id}: #{message}")
29
+ end
30
+
31
+ def logger
32
+ @logger ||= Quebert.logger
33
+ end
34
+
35
+ def backend
36
+ @backend ||= Quebert.config.backend
27
37
  end
28
38
  end
29
39
  end
data/quebert.gemspec CHANGED
@@ -5,13 +5,15 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{quebert}
8
- s.version = "0.0.0"
8
+ s.version = "0.0.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brad Gessler"]
12
- s.date = %q{2010-10-03}
12
+ s.date = %q{2010-10-04}
13
+ s.default_executable = %q{quebert}
13
14
  s.description = %q{A worker queue framework built around beanstalkd}
14
15
  s.email = %q{brad@bradgessler.com}
16
+ s.executables = ["quebert"]
15
17
  s.extra_rdoc_files = [
16
18
  "LICENSE",
17
19
  "README.rdoc"
@@ -24,23 +26,27 @@ Gem::Specification.new do |s|
24
26
  "README.rdoc",
25
27
  "Rakefile",
26
28
  "VERSION",
29
+ "bin/quebert",
27
30
  "lib/quebert.rb",
28
31
  "lib/quebert/async_sender.rb",
29
32
  "lib/quebert/backend.rb",
30
33
  "lib/quebert/backend/beanstalk.rb",
31
34
  "lib/quebert/backend/in_process.rb",
32
35
  "lib/quebert/backend/sync.rb",
36
+ "lib/quebert/command_line_runner.rb",
33
37
  "lib/quebert/configuration.rb",
34
38
  "lib/quebert/consumer.rb",
35
39
  "lib/quebert/consumer/base.rb",
36
40
  "lib/quebert/consumer/beanstalk.rb",
37
- "lib/quebert/daemonizing.rb",
38
41
  "lib/quebert/job.rb",
39
42
  "lib/quebert/support.rb",
43
+ "lib/quebert/support/pid_file.rb",
44
+ "lib/quebert/support/registry.rb",
40
45
  "lib/quebert/worker.rb",
41
46
  "quebert.gemspec",
42
47
  "spec/async_sender_spec.rb",
43
48
  "spec/backend_spec.rb",
49
+ "spec/command_line_runner_spec.rb",
44
50
  "spec/configuration_spec.rb",
45
51
  "spec/consumer_spec.rb",
46
52
  "spec/job_spec.rb",
@@ -58,6 +64,7 @@ Gem::Specification.new do |s|
58
64
  s.test_files = [
59
65
  "spec/async_sender_spec.rb",
60
66
  "spec/backend_spec.rb",
67
+ "spec/command_line_runner_spec.rb",
61
68
  "spec/configuration_spec.rb",
62
69
  "spec/consumer_spec.rb",
63
70
  "spec/job_spec.rb",
@@ -74,18 +81,18 @@ Gem::Specification.new do |s|
74
81
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
75
82
  s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
76
83
  s.add_runtime_dependency(%q<json>, [">= 0"])
77
- s.add_runtime_dependency(%q<daemons>, [">= 0"])
84
+ s.add_runtime_dependency(%q<optitron>, [">= 0"])
78
85
  s.add_runtime_dependency(%q<beanstalk-client>, [">= 0"])
79
86
  else
80
87
  s.add_dependency(%q<rspec>, [">= 1.2.9"])
81
88
  s.add_dependency(%q<json>, [">= 0"])
82
- s.add_dependency(%q<daemons>, [">= 0"])
89
+ s.add_dependency(%q<optitron>, [">= 0"])
83
90
  s.add_dependency(%q<beanstalk-client>, [">= 0"])
84
91
  end
85
92
  else
86
93
  s.add_dependency(%q<rspec>, [">= 1.2.9"])
87
94
  s.add_dependency(%q<json>, [">= 0"])
88
- s.add_dependency(%q<daemons>, [">= 0"])
95
+ s.add_dependency(%q<optitron>, [">= 0"])
89
96
  s.add_dependency(%q<beanstalk-client>, [">= 0"])
90
97
  end
91
98
  end
data/spec/backend_spec.rb CHANGED
@@ -6,9 +6,14 @@ describe Backend do
6
6
  end
7
7
 
8
8
  it "should register backends" do
9
- Quebert::Backend.register :twenty, 20
9
+ Quebert.backends.register :twenty, 20
10
10
  Quebert.backends[:twenty].should eql(20)
11
11
  end
12
+
13
+ it "should unregister backends" do
14
+ Quebert.backends.unregister :twenty
15
+ Quebert.backends[:twenty].should be_nil
16
+ end
12
17
  end
13
18
 
14
19
  describe Backend::InProcess do
@@ -0,0 +1,77 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe CommandLineRunner do
4
+ before(:all) do
5
+ Quebert.config.backend = Backend::InProcess.new
6
+ end
7
+
8
+ context "log-file" do
9
+ it "should write log file" do
10
+ clean_file 'log.log' do
11
+ lambda{
12
+ CommandLineRunner.dispatch(%w(worker --log-file=log.log))
13
+ }.should change { File.read('log.log') if File.exists?('log.log') }
14
+ end
15
+ end
16
+ end
17
+
18
+ context "pid-file" do
19
+ it "should write pid" do
20
+ clean_file 'pid.pid' do
21
+ File.exists?('pid').should be_false
22
+ CommandLineRunner.dispatch(%w(worker --pid-file=pid.pid))
23
+ Support::PidFile.read('pid.pid').should eql(Process.pid)
24
+ end
25
+ end
26
+
27
+ it "should remove stale" do
28
+ clean_file 'pid.pid', "-1" do
29
+ CommandLineRunner.dispatch(%w(worker --pid-file=pid.pid))
30
+ Support::PidFile.read('pid.pid').should eql(Process.pid)
31
+ end
32
+ end
33
+
34
+ it "should complain if the pid is already running" do
35
+ clean_file 'pid.pid', Process.pid do
36
+ lambda{
37
+ CommandLineRunner.dispatch(%w(worker --pid-file=pid.pid))
38
+ }.should raise_exception(Support::PidFile::ProcessRunning)
39
+ Support::PidFile.read('pid.pid').should eql(Process.pid)
40
+ end
41
+ end
42
+ end
43
+
44
+ context "config-file" do
45
+ it "should auto-detect rails environment file" do
46
+ clean_file './config/environment.rb', "raise 'RailsConfig'" do
47
+ lambda{
48
+ CommandLineRunner.dispatch(%w(worker))
49
+ }.should raise_exception('RailsConfig')
50
+ end
51
+ end
52
+
53
+ it "should run config file" do
54
+ clean_file './super_awesome.rb', "raise 'SuperAwesome'" do
55
+ lambda{
56
+ CommandLineRunner.dispatch(%w(worker --config-file=super_awesome.rb))
57
+ }.should raise_exception('SuperAwesome')
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ context "chdir" do
64
+ before(:each) do
65
+ @chdir = Dir.pwd
66
+ end
67
+
68
+ it "should change chdir" do
69
+ CommandLineRunner.dispatch(%w(worker --chdir=/))
70
+ Dir.pwd.should eql('/')
71
+ end
72
+
73
+ after(:each) do
74
+ Dir.chdir(@chdir)
75
+ end
76
+ end
77
+ end
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
  describe Configuration do
4
4
  context "from hash" do
5
5
  before(:all) do
6
- @config = Configuration.from_hash('backend' => 'beanstalk', 'host' => 'localhost:11300', 'tube' => 'quebert-config-test')
6
+ @config = Configuration.new.from_hash('backend' => 'beanstalk', 'host' => 'localhost:11300', 'tube' => 'quebert-config-test')
7
7
  end
8
8
 
9
9
  it "should configure backend" do
data/spec/spec_helper.rb CHANGED
@@ -7,10 +7,23 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
7
  require 'quebert'
8
8
  require 'spec'
9
9
  require 'spec/autorun'
10
+ require 'logger'
10
11
 
11
12
  Spec::Runner.configure do |config|
12
13
  end
13
14
 
14
15
  include Quebert
16
+ Quebert.config.logger = Logger.new('/dev/null') # Shhh...
15
17
 
16
- require 'jobs'
18
+ require 'jobs'
19
+
20
+ def clean_file(path, contents=nil, &block)
21
+ FileUtils.remove_entry(path) if File.exists?(path)
22
+ FileUtils.mkdir_p(File.dirname(path))
23
+ begin
24
+ File.open(path, 'w'){|f| f.write(contents == :empty ? nil : contents) } unless contents.nil?
25
+ block.call
26
+ ensure
27
+ FileUtils.remove_entry(path) if File.exists?(path) and path != './' # Yeah! This has happened before :(
28
+ end
29
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quebert
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 29
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 0
10
- version: 0.0.0
9
+ - 1
10
+ version: 0.0.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brad Gessler
@@ -15,8 +15,8 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-03 00:00:00 -07:00
19
- default_executable:
18
+ date: 2010-10-04 00:00:00 -07:00
19
+ default_executable: quebert
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: rspec
@@ -49,7 +49,7 @@ dependencies:
49
49
  type: :runtime
50
50
  version_requirements: *id002
51
51
  - !ruby/object:Gem::Dependency
52
- name: daemons
52
+ name: optitron
53
53
  prerelease: false
54
54
  requirement: &id003 !ruby/object:Gem::Requirement
55
55
  none: false
@@ -78,8 +78,8 @@ dependencies:
78
78
  version_requirements: *id004
79
79
  description: A worker queue framework built around beanstalkd
80
80
  email: brad@bradgessler.com
81
- executables: []
82
-
81
+ executables:
82
+ - quebert
83
83
  extensions: []
84
84
 
85
85
  extra_rdoc_files:
@@ -93,23 +93,27 @@ files:
93
93
  - README.rdoc
94
94
  - Rakefile
95
95
  - VERSION
96
+ - bin/quebert
96
97
  - lib/quebert.rb
97
98
  - lib/quebert/async_sender.rb
98
99
  - lib/quebert/backend.rb
99
100
  - lib/quebert/backend/beanstalk.rb
100
101
  - lib/quebert/backend/in_process.rb
101
102
  - lib/quebert/backend/sync.rb
103
+ - lib/quebert/command_line_runner.rb
102
104
  - lib/quebert/configuration.rb
103
105
  - lib/quebert/consumer.rb
104
106
  - lib/quebert/consumer/base.rb
105
107
  - lib/quebert/consumer/beanstalk.rb
106
- - lib/quebert/daemonizing.rb
107
108
  - lib/quebert/job.rb
108
109
  - lib/quebert/support.rb
110
+ - lib/quebert/support/pid_file.rb
111
+ - lib/quebert/support/registry.rb
109
112
  - lib/quebert/worker.rb
110
113
  - quebert.gemspec
111
114
  - spec/async_sender_spec.rb
112
115
  - spec/backend_spec.rb
116
+ - spec/command_line_runner_spec.rb
113
117
  - spec/configuration_spec.rb
114
118
  - spec/consumer_spec.rb
115
119
  - spec/job_spec.rb
@@ -155,6 +159,7 @@ summary: A worker queue framework built around beanstalkd
155
159
  test_files:
156
160
  - spec/async_sender_spec.rb
157
161
  - spec/backend_spec.rb
162
+ - spec/command_line_runner_spec.rb
158
163
  - spec/configuration_spec.rb
159
164
  - spec/consumer_spec.rb
160
165
  - spec/job_spec.rb
@@ -1,144 +0,0 @@
1
- require 'etc'
2
- require 'daemons'
3
- require 'fileutils'
4
-
5
- module Process
6
- # Returns +true+ the process identied by +pid+ is running.
7
- def running?(pid)
8
- Process.getpgid(pid) != -1
9
- rescue Errno::EPERM
10
- true
11
- rescue Errno::ESRCH
12
- false
13
- end
14
- module_function :running?
15
- end
16
-
17
- module Quebert
18
- module Daemonizable
19
- attr_accessor :pid_file, :log_file
20
-
21
- PidFileExist = Class.new(RuntimeError)
22
-
23
- def self.included(base)
24
- base.extend ClassMethods
25
- end
26
-
27
- def daemonize
28
- raise ArgumentError, 'You must specify a pid_file to daemonize' unless pid_file
29
-
30
- remove_stale_pid_file
31
-
32
- pwd = Dir.pwd # Current directory is changed during daemonization, so store it
33
- # HACK we need to create the directory before daemonization to prevent a bug under 1.9
34
- # ignoring all signals when the directory is created after daemonization.
35
- FileUtils.mkdir_p File.dirname(pid_file)
36
- # Daemonize.daemonize(File.expand_path(@log_file), "quebert worker")
37
- Daemonize.daemonize(File.expand_path(@log_file), "quebert")
38
- Dir.chdir(pwd)
39
- write_pid_file
40
- end
41
-
42
- def pid
43
- File.exist?(pid_file) ? open(pid_file).read.to_i : nil
44
- end
45
-
46
- # Register a proc to be called to restart the server.
47
- def on_restart(&block)
48
- @on_restart = block
49
- end
50
-
51
- # Restart the server.
52
- def restart
53
- if @on_restart
54
- log '>> Restarting ...'
55
- stop
56
- remove_pid_file
57
- @on_restart.call
58
- exit!
59
- end
60
- end
61
-
62
- module ClassMethods
63
- # Send a QUIT or INT (if timeout is +0+) signal the process which
64
- # PID is stored in +pid_file+.
65
- # If the process is still running after +timeout+, KILL signal is
66
- # sent.
67
- def kill(pid_file, timeout=60)
68
- if timeout == 0
69
- send_signal('INT', pid_file, timeout)
70
- else
71
- send_signal('QUIT', pid_file, timeout)
72
- end
73
- end
74
-
75
- # Restart the server by sending HUP signal.
76
- def restart(pid_file)
77
- send_signal('HUP', pid_file)
78
- end
79
-
80
- # Send a +signal+ to the process which PID is stored in +pid_file+.
81
- def send_signal(signal, pid_file, timeout=60)
82
- if pid = read_pid_file(pid_file)
83
- Logging.log "Sending #{signal} signal to process #{pid} ... "
84
- Process.kill(signal, pid)
85
- Timeout.timeout(timeout) do
86
- sleep 0.1 while Process.running?(pid)
87
- end
88
- else
89
- Logging.log "Can't stop process, no PID found in #{pid_file}"
90
- end
91
- rescue Timeout::Error
92
- Logging.log "Timeout!"
93
- force_kill pid_file
94
- rescue Interrupt
95
- force_kill pid_file
96
- rescue Errno::ESRCH # No such process
97
- Logging.log "process not found!"
98
- force_kill pid_file
99
- end
100
-
101
- def force_kill(pid_file)
102
- if pid = read_pid_file(pid_file)
103
- Logging.log "Sending KILL signal to process #{pid} ... "
104
- Process.kill("KILL", pid)
105
- File.delete(pid_file) if File.exist?(pid_file)
106
- else
107
- Logging.log "Can't stop process, no PID found in #{pid_file}"
108
- end
109
- end
110
-
111
- def read_pid_file(file)
112
- if File.file?(file) && pid = File.read(file)
113
- pid.to_i
114
- else
115
- nil
116
- end
117
- end
118
- end
119
-
120
- protected
121
- def remove_pid_file
122
- File.delete(pid_file) if pid_file && File.exists?(pid_file)
123
- end
124
-
125
- def write_pid_file
126
- log ">> Writing PID to #{pid_file}"
127
- open(pid_file,"w") { |f| f.write(Process.pid) }
128
- File.chmod(0644, pid_file)
129
- end
130
-
131
- # If PID file is stale, remove it.
132
- def remove_stale_pid_file
133
- if File.exist?(pid_file)
134
- if pid && Process.running?(pid)
135
- raise PidFileExist, "#{pid_file} already exists, seems like it's already running (process ID: #{pid}). " +
136
- "Stop the process or delete #{pid_file}."
137
- else
138
- log ">> Deleting stale PID file #{pid_file}"
139
- remove_pid_file
140
- end
141
- end
142
- end
143
- end
144
- end