Created
March 10, 2011 13:14
-
-
Save vjt/864073 to your computer and use it in GitHub Desktop.
The simplest foreground Rails service framework you'll ever see
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# vim: ft=ruby | |
# | |
# Usage: | |
# | |
# Save as ./script/service | |
# | |
# Create your ./script/job containing: | |
# | |
# load './script/service' | |
# Service.new do | |
# log 'Starting marvellous service' | |
# on_graceful_exit {log 'Bye bye!'} | |
# loop {puts 'something'; sleep 10} | |
# end | |
# | |
# chmod +x ./script/job | |
# ./script/job | |
# | |
# Run it under daemontools, runit, upstart, God, Bluepill, launchd, whatever. | |
# Read the source and have fun. Tests and kudos are welcome :-p | |
# | |
# - vjt Thu Mar 10 14:06:17 CET 2011 | |
# | |
require 'pathname' | |
class Service | |
def initialize(name = nil, &service) | |
@name = name || File.basename($0) | |
check_pidfile | |
write_pidfile | |
setup_signals | |
boot! service | |
rescue StandardError => e | |
@horrible_proc.call if @horrible_proc rescue nil | |
fail! "Uncaught exception #{e.inspect}\n #{e.backtrace.join("\n ")}" | |
end | |
protected | |
def log(*message) | |
puts [Time.now, "[#$$]", *message].join(' ') | |
end | |
def fail!(message) | |
log message | |
exit 1 | |
end | |
def graceful(reason) | |
@graceful_proc.call if @graceful_proc | |
log "Exiting via #{reason}" | |
exit 0 | |
end | |
def on_graceful_exit(&block) | |
@graceful_proc = block | |
end | |
def on_horrible_exit(&block) | |
@horrible_proc = block | |
end | |
private | |
def check_pidfile | |
@pidfile = Pathname("tmp/pids/#@name.pid") | |
return unless @pidfile.exist? | |
pid = @pidfile.read.to_i | |
begin | |
if Process.kill 0, pid | |
fail! "#@name is already running as PID #{pid}" | |
end | |
rescue Errno::ESRCH, Errno::EPERM => e | |
# Process does not exist, or is owned by another user: | |
# either way, it's a stale pifile: remove it and go on | |
log "Removing stale pidfile [#{pid}]" | |
@pidfile.unlink | |
rescue Errno | |
fail! "#@pidfile already exist: is #@name already running as PID #{pid}?" | |
end | |
end | |
def write_pidfile | |
@pidfile.open('w+') {|f| f.write($$)} | |
end | |
def setup_signals | |
trap('EXIT') { @pidfile.unlink } # Reminds me of Erlang times... | |
trap('TERM') { graceful 'SIGTERM' } | |
trap('INT') { graceful 'SIGINT' } | |
end | |
def boot!(service) | |
log 'Loading application...' | |
require 'config/environment' | |
instance_eval &service | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment