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.



24
25
26
27
28
29
# File 'lib/spring/server.rb', line 24

def initialize(env = Env.new)
  @env          = env
  @applications = Hash.new { |h, k| h[k] = ApplicationManager.new(self, k) }
  @pidfile      = env.pidfile_path.open('a')
  @mutex        = Mutex.new
end

Instance Attribute Details

#envObject (readonly)

Returns the value of attribute env.



22
23
24
# File 'lib/spring/server.rb', line 22

def env
  @env
end

Class Method Details

.bootObject



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

def self.boot
  new.boot
end

Instance Method Details

#application_startingObject



135
136
137
# File 'lib/spring/server.rb', line 135

def application_starting
  @mutex.synchronize { exit if env.bundle_mtime != @bundle_mtime }
end

#bootObject



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/spring/server.rb', line 31

def boot
  write_pidfile
  set_pgid
  ignore_signals
  set_exit_hook
  redirect_output
  set_process_title
  watch_bundle

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

#ignore_signalsObject

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



81
82
83
# File 'lib/spring/server.rb', line 81

def ignore_signals
  IGNORE_SIGNALS.each { |sig| trap(sig,  "IGNORE") }
end

#rails_env_for(args, default_rails_env) ⇒ Object



62
63
64
65
66
67
68
69
70
# File 'lib/spring/server.rb', line 62

def rails_env_for(args, default_rails_env)
  command = Spring.command(args.first)

  if command.respond_to?(:env)
    env = command.env(args.drop(1))
  end

  env || default_rails_env
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.



118
119
120
121
122
123
# File 'lib/spring/server.rb', line 118

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
53
54
55
56
57
58
59
60
# File 'lib/spring/server.rb', line 44

def serve(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)
    client.puts
    client.puts @applications[rails_env_for(args, default_rails_env)].run(app_client)
  else
    client.close
  end
rescue SocketError => e
  raise e unless client.eof?
end

#set_exit_hookObject



85
86
87
88
89
90
# File 'lib/spring/server.rb', line 85

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_pgidObject

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).



75
76
77
# File 'lib/spring/server.rb', line 75

def set_pgid
  Process.setpgid(0, SID.pgid)
end

#set_process_titleObject



125
126
127
128
129
# File 'lib/spring/server.rb', line 125

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

#shutdownObject



92
93
94
95
96
97
98
# File 'lib/spring/server.rb', line 92

def shutdown
  @applications.values.each(&:stop)

  [env.socket_path, env.pidfile_path].each do |path|
    path.unlink if path.exist?
  end
end

#watch_bundleObject



131
132
133
# File 'lib/spring/server.rb', line 131

def watch_bundle
  @bundle_mtime = env.bundle_mtime
end

#write_pidfileObject



100
101
102
103
104
105
106
107
108
# File 'lib/spring/server.rb', line 100

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