class Spring::Server

Attributes

env[R]

Public Class Methods

boot() click to toggle source
# File lib/spring/server.rb, line 13
def self.boot
  new.boot
end
new(env = Env.new) click to toggle source
# File lib/spring/server.rb, line 19
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

Public Instance Methods

boot() click to toggle source
# File lib/spring/server.rb, line 30
def boot
  Spring.verify_environment

  write_pidfile
  set_pgid
  ignore_signals
  set_exit_hook
  set_process_title
  start_server
end
ignore_signals() click to toggle source

Ignore SIGINT and SIGQUIT otherwise the user typing ^C or ^\ on the command line will kill the server/application.

# File lib/spring/server.rb, line 83
def ignore_signals
  IGNORE_SIGNALS.each { |sig| trap(sig, "IGNORE") }
end
log(message) click to toggle source
# File lib/spring/server.rb, line 26
def log(message)
  env.log "[server] #{message}"
end
rails_env_for(args, default_rails_env) click to toggle source
# File lib/spring/server.rb, line 70
def rails_env_for(args, default_rails_env)
  Spring.command(args.first).env(args.drop(1)) || default_rails_env
end
redirect_output() click to toggle source

We need to redirect STDOUT and STDERR, otherwise the server will keep the original FDs open which would break piping. (e.g. `spring rake -T | grep db` would hang forever because the server would keep the stdout FD open.)

# File lib/spring/server.rb, line 120
def redirect_output
  [STDOUT, STDERR].each { |stream| stream.reopen(env.log_file) }
end
serve(client) click to toggle source
# File lib/spring/server.rb, line 47
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
set_exit_hook() click to toggle source
# File lib/spring/server.rb, line 87
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
set_pgid() click to toggle source

Boot the server into the process group of the current session. This will cause it to be automatically killed once the session ends (i.e. when the user closes their terminal).

# File lib/spring/server.rb, line 77
def set_pgid
  Process.setpgid(0, SID.pgid)
end
set_process_title() click to toggle source
# File lib/spring/server.rb, line 124
def set_process_title
  ProcessTitleUpdater.run { |distance|
    "spring server | #{env.app_name} | started #{distance} ago"
  }
end
shutdown() click to toggle source
# File lib/spring/server.rb, line 94
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
start_server() click to toggle source
# File lib/spring/server.rb, line 41
def start_server
  server = UNIXServer.open(env.socket_name)
  log "started on #{env.socket_name}"
  loop { serve server.accept }
end
write_pidfile() click to toggle source
# File lib/spring/server.rb, line 106
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