class Spring::Application

def active_record_configured?

def active_record_configured?
  defined?(ActiveRecord::Base) && ActiveRecord::Base.configurations.any?
end

def app_env

def app_env
  ENV['RAILS_ENV']
end

def app_name

def app_name
  spring_env.app_name
end

def connect_database

def connect_database
  ActiveRecord::Base.establish_connection if active_record_configured?
end

def disconnect_database

def disconnect_database
  ActiveRecord::Base.remove_connection if active_record_configured?
end

def eager_preload

def eager_preload
  with_pty { preload }
end

def exit

def exit
  state :exiting
  manager.shutdown(:RDWR)
  exit_if_finished
  sleep
end

def exit_if_finished

def exit_if_finished
  @mutex.synchronize {
    Kernel.exit if exiting? && @waiting.empty?
  }
end

def exiting?

def exiting?
  @state == :exiting
end

def initialize(manager, original_env)

def initialize(manager, original_env)
  @manager      = manager
  @original_env = original_env
  @spring_env   = Env.new
  @mutex        = Mutex.new
  @waiting      = Set.new
  @preloaded    = false
  @state        = :initialized
  @interrupt    = IO.pipe
end

def initialized?

def initialized?
  @state == :initialized
end

def invoke_after_fork_callbacks

def invoke_after_fork_callbacks
  Spring.after_fork_callbacks.each do |callback|
    callback.call
  end
end

def loaded_application_features

def loaded_application_features
  root = Spring.application_root_path.to_s
  $LOADED_FEATURES.select { |f| f.start_with?(root) }
end

def log(message)

def log(message)
  spring_env.log "[application:#{app_env}] #{message}"
end

def preload

def preload
  log "preloading app"
  begin
    require "spring/commands"
  ensure
    start_watcher
  end
  require Spring.application_root_path.join("config", "application")
  # config/environments/test.rb will have config.cache_classes = true. However
  # we want it to be false so that we can reload files. This is a hack to
  # override the effect of config.cache_classes = true. We can then actually
  # set config.cache_classes = false after loading the environment.
  Rails::Application.initializer :initialize_dependency_mechanism, group: :all do
    ActiveSupport::Dependencies.mechanism = :load
  end
  require Spring.application_root_path.join("config", "environment")
  @original_cache_classes = Rails.application.config.cache_classes
  Rails.application.config.cache_classes = false
  disconnect_database
  @preloaded = :success
rescue Exception => e
  @preloaded = :failure
  watcher.add e.backtrace.map { |line| line.match(/^(.*)\:\d+\:in /)[1] }
  raise e unless initialized?
ensure
  watcher.add loaded_application_features
  watcher.add Spring.gemfile, "#{Spring.gemfile}.lock"
  if defined?(Rails) && Rails.application
    watcher.add Rails.application.paths["config/initializers"]
    watcher.add Rails.application.paths["config/database"]
    if secrets_path = Rails.application.paths["config/secrets"]
      watcher.add secrets_path
    end
  end
end

def preload_failed?

def preload_failed?
  @preloaded == :failure
end

def preloaded?

def preloaded?
  @preloaded
end

def print_exception(stream, error)

def print_exception(stream, error)
  first, rest = error.backtrace.first, error.backtrace.drop(1)
  stream.puts("#{first}: #{error} (#{error.class})")
  rest.each { |line| stream.puts("\tfrom #{line}") }
end

def reset_streams

def reset_streams
  [STDOUT, STDERR].each { |stream| stream.reopen(spring_env.log_file) }
  STDIN.reopen("/dev/null")
end

def run

def run
  state :running
  manager.puts
  loop do
    IO.select [manager, @interrupt.first]
    if terminating? || watcher_stale? || preload_failed?
      exit
    else
      serve manager.recv_io(UNIXSocket)
    end
  end
end

def serve(client)

def serve(client)
  log "got client"
  manager.puts
  stdout, stderr, stdin = streams = 3.times.map { client.recv_io }
  [STDOUT, STDERR, STDIN].zip(streams).each { |a, b| a.reopen(b) }
  preload unless preloaded?
  args, env = JSON.load(client.read(client.gets.to_i)).values_at("args", "env")
  command   = Spring.command(args.shift)
  connect_database
  setup command
  if Rails.application.reloaders.any?(&:updated?)
    ActionDispatch::Reloader.cleanup!
    ActionDispatch::Reloader.prepare!
  end
  pid = fork {
    IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
    trap("TERM", "DEFAULT")
    ARGV.replace(args)
    $0 = command.exec_name
    # Delete all env vars which are unchanged from before spring started
    original_env.each { |k, v| ENV.delete k if ENV[k] == v }
    # Load in the current env vars, except those which *were* changed when spring started
    env.each { |k, v| ENV[k] ||= v }
    # requiring is faster, so if config.cache_classes was true in
    # the environment's config file, then we can respect that from
    # here on as we no longer need constant reloading.
    if @original_cache_classes
      ActiveSupport::Dependencies.mechanism = :require
      Rails.application.config.cache_classes = true
    end
    connect_database
    srand
    invoke_after_fork_callbacks
    shush_backtraces
    command.call
  }
  disconnect_database
  reset_streams
  log "forked #{pid}"
  manager.puts pid
  wait pid, streams, client
rescue Exception => e
  log "exception: #{e}"
  manager.puts unless pid
  if streams && !e.is_a?(SystemExit)
    print_exception(stderr, e)
    streams.each(&:close)
  end
  client.puts(1) if pid
  client.close
end

def setup(command)

load the helper file once and have it cached.
main process so that they are cached. For example a test command wants to
The command might need to require some files in the
def setup(command)
  if command.setup
    watcher.add loaded_application_features # loaded features may have changed
  end
end

def shush_backtraces

This feels very naughty
def shush_backtraces
  Kernel.module_eval do
    old_raise = Kernel.method(:raise)
    remove_method :raise
    define_method :raise do |*args|
      begin
        old_raise.call(*args)
      ensure
        if $!
          lib = File.expand_path("..", __FILE__)
          $!.backtrace.reject! { |line| line.start_with?(lib) }
        end
      end
    end
    private :raise
  end
end

def start_watcher

def start_watcher
  @watcher = Spring.watcher
  @watcher.on_stale { state! :watcher_stale }
  @watcher.start
end

def state(val)

def state(val)
  return if exiting?
  log "#{@state} -> #{val}"
  @state = val
end

def state!(val)

def state!(val)
  state val
  @interrupt.last.write "."
end

def terminate

def terminate
  if exiting?
    # Ensure that we do not ignore subsequent termination attempts
    log "forced exit"
    @waiting.each { |pid| Process.kill("TERM", pid) }
    Kernel.exit
  else
    state! :terminating
  end
end

def terminating?

def terminating?
  @state == :terminating
end

def wait(pid, streams, client)

def wait(pid, streams, client)
  @mutex.synchronize { @waiting << pid }
  # Wait in a separate thread so we can run multiple commands at once
  Thread.new {
    begin
      _, status = Process.wait2 pid
      log "#{pid} exited with #{status.exitstatus}"
      streams.each(&:close)
      client.puts(status.exitstatus)
      client.close
    ensure
      @mutex.synchronize { @waiting.delete pid }
      exit_if_finished
    end
  }
end

def watcher_stale?

def watcher_stale?
  @state == :watcher_stale
end

def with_pty

def with_pty
  PTY.open do |master, slave|
    [STDOUT, STDERR, STDIN].each { |s| s.reopen slave }
    Thread.new { master.read }
    yield
    reset_streams
  end
end