quebert 0.0.0 → 0.0.1

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/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