class Spring::Server
def self.boot
def self.boot new.boot end
def boot
def boot Spring.verify_environment write_pidfile set_pgid ignore_signals set_exit_hook set_process_title start_server end
def ignore_signals
Ignore SIGINT and SIGQUIT otherwise the user typing ^C or ^\ on the command line
def ignore_signals IGNORE_SIGNALS.each { |sig| trap(sig, "IGNORE") } end
def initialize(env = Env.new)
def initialize(env = Env.new) @env = env @applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k) } @pidfile = env.pidfile_path.open('a') @mutex = Mutex.new end
def log(message)
def log(message) env.log "[server] #{message}" end
def rails_env_for(args, default_rails_env)
def rails_env_for(args, default_rails_env) Spring.command(args.first).env(args.drop(1)) || default_rails_env end
def redirect_output
`spring rake -T | grep db` would hang forever because the server
keep the original FDs open which would break piping. (e.g.
We need to redirect STDOUT and STDERR, otherwise the server will
def redirect_output [STDOUT, STDERR].each { |stream| stream.reopen(env.log_file) } end
def serve(client)
def serve(client) log "accepted client" client.puts env.version app_client = client.recv_io command = JSON.load(client.read(client.gets.to_i)) args, default_rails_env = command.values_at('args', 'default_rails_env') if Spring.command?(args.first) log "running command #{args.first}" client.puts client.puts @applications[rails_env_for(args, default_rails_env)].run(app_client) else log "command not found #{args.first}" client.close end rescue SocketError => e raise e unless client.eof? ensure redirect_output end
def set_exit_hook
def set_exit_hook server_pid = Process.pid # We don't want this hook to run in any forks of the current process at_exit { shutdown if Process.pid == server_pid } end
def set_pgid
This will cause it to be automatically killed once the session
Boot the server into the process group of the current session.
def set_pgid Process.setpgid(0, SID.pgid) end
def set_process_title
def set_process_title ProcessTitleUpdater.run { |distance| "spring server | #{env.app_name} | started #{distance} ago" } end
def shutdown
def shutdown log "shutting down" [env.socket_path, env.pidfile_path].each do |path| if path.exist? path.unlink rescue nil end end @applications.values.map { |a| Thread.new { a.stop } }.map(&:join) end
def start_server
def start_server server = UNIXServer.open(env.socket_name) log "started on #{env.socket_name}" loop { serve server.accept } end
def write_pidfile
def write_pidfile if @pidfile.flock(File::LOCK_EX | File::LOCK_NB) @pidfile.truncate(0) @pidfile.write("#{Process.pid}\n") @pidfile.fsync else exit 1 end end