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.



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

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.



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

def env
  @env
end

Class Method Details

.bootObject



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

def self.boot
  new.boot
end

Instance Method Details

#application_startingObject



148
149
150
# File 'lib/spring/server.rb', line 148

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

#bootObject



36
37
38
39
40
41
42
43
44
45
46
# File 'lib/spring/server.rb', line 36

def boot
  Spring.verify_environment

  write_pidfile
  set_pgid
  ignore_signals
  set_exit_hook
  set_process_title
  watch_bundle
  start_server
end

#ignore_signalsObject

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



99
100
101
# File 'lib/spring/server.rb', line 99

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

#log(message) ⇒ Object



32
33
34
# File 'lib/spring/server.rb', line 32

def log(message)
  env.log "[server] #{message}"
end

#rails_env_for(args, default_rails_env) ⇒ Object



80
81
82
83
84
85
86
87
88
# File 'lib/spring/server.rb', line 80

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



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

def redirect_output
  [STDOUT, STDERR].each { |stream| stream.reopen(env.log_file) }
end

#serve(client) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/spring/server.rb', line 57

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_hookObject



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

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



93
94
95
# File 'lib/spring/server.rb', line 93

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

#set_process_titleObject



138
139
140
141
142
# File 'lib/spring/server.rb', line 138

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

#shutdownObject



110
111
112
113
114
115
116
117
118
# File 'lib/spring/server.rb', line 110

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

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

#start_serverObject



48
49
50
51
52
53
54
55
# File 'lib/spring/server.rb', line 48

def start_server
  server = UNIXServer.open(env.socket_name)
  log "started on #{env.socket_name}"
rescue Errno::EPERM
  raise TmpUnwritable.new(env.tmp_path)
else
  loop { serve server.accept }
end

#watch_bundleObject



144
145
146
# File 'lib/spring/server.rb', line 144

def watch_bundle
  @bundle_mtime = env.bundle_mtime
end

#write_pidfileObject



120
121
122
123
124
125
126
127
128
# File 'lib/spring/server.rb', line 120

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