Class: Spring::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/spring/server.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env = Env.new) ⇒ Server

Returns a new instance of Server.



19
20
21
22
23
# 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')
end

Instance Attribute Details

#envObject (readonly)

Returns the value of attribute env.



17
18
19
# File 'lib/spring/server.rb', line 17

def env
  @env
end

Class Method Details

.bootObject



13
14
15
# File 'lib/spring/server.rb', line 13

def self.boot
  new.boot
end

Instance Method Details

#bootObject



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/spring/server.rb', line 25

def boot
  # 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).
  Process.setpgid(0, SID.pgid)

  # Ignore SIGINT and SIGQUIT otherwise the user typing ^C or ^\ on the command line
  # will kill the server/application.
  IGNORE_SIGNALS.each { |sig| trap(sig,  "IGNORE") }

  set_exit_hook
  write_pidfile
  redirect_output
  set_process_title

  server = UNIXServer.open(env.socket_name)
  loop { serve server.accept }
end

#redirect_outputObject

We can’t leave STDOUT, STDERR as they as because then they will never get closed for the lifetime of the server. This means that piping, e.g. “spring rake -T | grep db” won’t work correctly because grep will hang while waiting for its stdin to reach EOF.

However we do want server output to go to the terminal in case there are exceptions etc, so we just open the current terminal device directly.



88
89
90
91
92
93
# File 'lib/spring/server.rb', line 88

def redirect_output
  # ruby doesn't expose ttyname()
  file = open(STDIN.tty? ? `tty`.chomp : "/dev/null", "a")
  STDOUT.reopen(file)
  STDERR.reopen(file)
end

#serve(client) ⇒ Object



44
45
46
47
48
49
50
51
52
# File 'lib/spring/server.rb', line 44

def serve(client)
  client.puts env.version
  app_client = client.recv_io
  rails_env  = client.gets.chomp

  client.puts @applications[rails_env].run(app_client)
rescue SocketError => e
  raise e unless client.eof?
end

#set_exit_hookObject



54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/spring/server.rb', line 54

def set_exit_hook
  server_pid = Process.pid

  at_exit do
    # We don't want this hook to run in any forks of the current process
    if Process.pid == server_pid
      [env.socket_path, env.pidfile_path].each do |path|
        path.unlink if path.exist?
      end

      @applications.values.each(&:stop)
    end
  end
end

#set_process_titleObject



95
96
97
98
99
# File 'lib/spring/server.rb', line 95

def set_process_title
  ProcessTitleUpdater.run { |distance|
    "spring server | #{env.app_name} | started #{distance} ago"
  }
end

#write_pidfileObject



69
70
71
72
73
74
75
76
77
78
# File 'lib/spring/server.rb', line 69

def write_pidfile
  if @pidfile.flock(File::LOCK_EX | File::LOCK_NB)
    @pidfile.truncate(0)
    @pidfile.write("#{Process.pid}\n")
    @pidfile.fsync
  else
    $stderr.puts "#{@pidfile.path} is locked; it looks like a server is already running"
    exit 1
  end
end