spring 2.0.2 → 4.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE.txt +1 -0
- data/README.md +66 -39
- data/lib/spring/application/boot.rb +9 -3
- data/lib/spring/application.rb +80 -42
- data/lib/spring/application_manager.rb +10 -5
- data/lib/spring/binstub.rb +0 -20
- data/lib/spring/client/binstub.rb +32 -40
- data/lib/spring/client/help.rb +1 -1
- data/lib/spring/client/rails.rb +4 -4
- data/lib/spring/client/run.rb +50 -12
- data/lib/spring/client/stop.rb +1 -1
- data/lib/spring/commands.rb +11 -4
- data/lib/spring/configuration.rb +33 -2
- data/lib/spring/env.rb +3 -4
- data/lib/spring/errors.rb +1 -1
- data/lib/spring/json.rb +27 -30
- data/lib/spring/process_title_updater.rb +1 -1
- data/lib/spring/server.rb +16 -6
- data/lib/spring/version.rb +1 -1
- data/lib/spring/watcher/abstract.rb +12 -12
- data/lib/spring/watcher/polling.rb +2 -2
- metadata +10 -59
- data/lib/spring/sid.rb +0 -42
- data/lib/spring/test/acceptance_test.rb +0 -558
- data/lib/spring/test/application.rb +0 -229
- data/lib/spring/test/application_generator.rb +0 -144
- data/lib/spring/test/rails_version.rb +0 -23
- data/lib/spring/test/watcher_test.rb +0 -194
- data/lib/spring/test.rb +0 -18
@@ -1,56 +1,48 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
1
|
module Spring
|
4
2
|
module Client
|
5
3
|
class Binstub < Command
|
6
|
-
SHEBANG = /\#\!.*\n
|
4
|
+
SHEBANG = /\#\!.*\n(\#.*\n)*/
|
7
5
|
|
8
|
-
# If loading the bin/spring file works, it'll run
|
6
|
+
# If loading the bin/spring file works, it'll run Spring which will
|
9
7
|
# eventually call Kernel.exit. This means that in the client process
|
10
|
-
# we will never execute the lines after this block. But if the
|
8
|
+
# we will never execute the lines after this block. But if the Spring
|
11
9
|
# client is not invoked for whatever reason, then the Kernel.exit won't
|
12
10
|
# happen, and so we'll fall back to the lines after this block, which
|
13
11
|
# should cause the "unsprung" version of the command to run.
|
14
|
-
LOADER =
|
15
|
-
|
16
|
-
|
17
|
-
rescue LoadError => e
|
18
|
-
raise unless e.message.include?('spring')
|
19
|
-
end
|
20
|
-
CODE
|
12
|
+
LOADER = <<~CODE
|
13
|
+
load File.expand_path("spring", __dir__)
|
14
|
+
CODE
|
21
15
|
|
22
16
|
# The defined? check ensures these lines don't execute when we load the
|
23
17
|
# binstub from the application process. Which means that in the application
|
24
18
|
# process we'll execute the lines which come after the LOADER block, which
|
25
19
|
# is what we want.
|
26
|
-
SPRING =
|
27
|
-
#!/usr/bin/env ruby
|
28
|
-
|
29
|
-
# This file loads
|
30
|
-
# It gets overwritten when you run the `spring binstub` command.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
44
|
-
CODE
|
20
|
+
SPRING = <<~CODE
|
21
|
+
#!/usr/bin/env ruby
|
22
|
+
|
23
|
+
# This file loads Spring without loading other gems in the Gemfile in order to be fast.
|
24
|
+
# It gets overwritten when you run the `spring binstub` command.
|
25
|
+
|
26
|
+
if !defined?(Spring) && [nil, "development", "test"].include?(ENV["RAILS_ENV"])
|
27
|
+
require "bundler"
|
28
|
+
|
29
|
+
Bundler.locked_gems.specs.find { |spec| spec.name == "spring" }&.tap do |spring|
|
30
|
+
Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
|
31
|
+
gem "spring", spring.version
|
32
|
+
require "spring/binstub"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
CODE
|
45
36
|
|
46
37
|
OLD_BINSTUB = %{if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?}
|
47
38
|
|
48
39
|
BINSTUB_VARIATIONS = Regexp.union [
|
49
|
-
%{
|
40
|
+
%{load File.expand_path("spring", __dir__)\n},
|
41
|
+
%{begin\n load File.expand_path('../spring', __FILE__)\nrescue LoadError => e\n raise unless e.message.include?('spring')\nend\n},
|
50
42
|
%{begin\n load File.expand_path('../spring', __FILE__)\nrescue LoadError\nend\n},
|
51
43
|
%{begin\n spring_bin_path = File.expand_path('../spring', __FILE__)\n load spring_bin_path\nrescue LoadError => e\n raise unless e.message.end_with? spring_bin_path, 'spring/binstub'\nend\n},
|
52
44
|
LOADER
|
53
|
-
]
|
45
|
+
].map { |binstub| /#{Regexp.escape(binstub).gsub("'", "['\"]")}/ }
|
54
46
|
|
55
47
|
class Item
|
56
48
|
attr_reader :command, :existing
|
@@ -79,7 +71,7 @@ CODE
|
|
79
71
|
generate(fallback)
|
80
72
|
status "upgraded"
|
81
73
|
elsif existing.include?(LOADER)
|
82
|
-
status "
|
74
|
+
status "Spring already present"
|
83
75
|
elsif existing =~ BINSTUB_VARIATIONS
|
84
76
|
upgraded = existing.sub(BINSTUB_VARIATIONS, LOADER)
|
85
77
|
File.write(command.binstub, upgraded)
|
@@ -94,15 +86,15 @@ CODE
|
|
94
86
|
end
|
95
87
|
|
96
88
|
File.write(command.binstub, "#{head}#{shebang}#{LOADER}#{tail}")
|
97
|
-
status "
|
89
|
+
status "Spring inserted"
|
98
90
|
else
|
99
|
-
status "doesn't appear to be ruby, so cannot use
|
91
|
+
status "doesn't appear to be ruby, so cannot use Spring", $stderr
|
100
92
|
exit 1
|
101
93
|
end
|
102
94
|
end
|
103
95
|
else
|
104
96
|
generate
|
105
|
-
status "generated with
|
97
|
+
status "generated with Spring"
|
106
98
|
end
|
107
99
|
end
|
108
100
|
|
@@ -119,7 +111,7 @@ CODE
|
|
119
111
|
def remove
|
120
112
|
if existing
|
121
113
|
File.write(command.binstub, existing.sub(BINSTUB_VARIATIONS, ""))
|
122
|
-
status "
|
114
|
+
status "Spring removed"
|
123
115
|
end
|
124
116
|
end
|
125
117
|
end
|
@@ -127,7 +119,7 @@ CODE
|
|
127
119
|
attr_reader :bindir, :items
|
128
120
|
|
129
121
|
def self.description
|
130
|
-
"Generate
|
122
|
+
"Generate Spring based binstubs. Use --all to generate a binstub for all known commands. Use --remove to revert."
|
131
123
|
end
|
132
124
|
|
133
125
|
def self.rails_command
|
@@ -147,7 +139,7 @@ CODE
|
|
147
139
|
@mode = :add
|
148
140
|
@items = args.drop(1)
|
149
141
|
.map { |name| find_commands name }
|
150
|
-
.
|
142
|
+
.flatten.uniq
|
151
143
|
.map { |command| Item.new(command) }
|
152
144
|
end
|
153
145
|
|
data/lib/spring/client/help.rb
CHANGED
@@ -32,7 +32,7 @@ module Spring
|
|
32
32
|
def formatted_help
|
33
33
|
["Version: #{env.version}\n",
|
34
34
|
"Usage: spring COMMAND [ARGS]\n",
|
35
|
-
*command_help("
|
35
|
+
*command_help("Spring itself", spring_commands),
|
36
36
|
'',
|
37
37
|
*command_help("your application", application_commands)].join("\n")
|
38
38
|
end
|
data/lib/spring/client/rails.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
require "set"
|
2
|
-
|
3
1
|
module Spring
|
4
2
|
module Client
|
5
3
|
class Rails < Command
|
6
|
-
COMMANDS =
|
4
|
+
COMMANDS = %w(console runner generate destroy test)
|
7
5
|
|
8
6
|
ALIASES = {
|
9
7
|
"c" => "console",
|
@@ -14,7 +12,7 @@ module Spring
|
|
14
12
|
}
|
15
13
|
|
16
14
|
def self.description
|
17
|
-
"Run a rails command. The following sub commands will use
|
15
|
+
"Run a rails command. The following sub commands will use Spring: #{COMMANDS.to_a.join ', '}."
|
18
16
|
end
|
19
17
|
|
20
18
|
def call
|
@@ -22,6 +20,8 @@ module Spring
|
|
22
20
|
|
23
21
|
if COMMANDS.include?(command_name)
|
24
22
|
Run.call(["rails_#{command_name}", *args.drop(2)])
|
23
|
+
elsif command_name&.start_with?("db:") && !command_name.start_with?("db:system")
|
24
|
+
Run.call(["rake", *args.drop(1)])
|
25
25
|
else
|
26
26
|
require "spring/configuration"
|
27
27
|
ARGV.shift
|
data/lib/spring/client/run.rb
CHANGED
@@ -6,8 +6,6 @@ module Spring
|
|
6
6
|
module Client
|
7
7
|
class Run < Command
|
8
8
|
FORWARDED_SIGNALS = %w(INT QUIT USR1 USR2 INFO WINCH) & Signal.list.keys
|
9
|
-
CONNECT_TIMEOUT = 1
|
10
|
-
BOOT_TIMEOUT = 20
|
11
9
|
|
12
10
|
attr_reader :server
|
13
11
|
|
@@ -44,7 +42,7 @@ module Spring
|
|
44
42
|
require "spring/commands"
|
45
43
|
|
46
44
|
if Spring.command?(args.first)
|
47
|
-
# Command installed since
|
45
|
+
# Command installed since Spring started
|
48
46
|
stop_server
|
49
47
|
cold_run
|
50
48
|
else
|
@@ -73,8 +71,8 @@ module Spring
|
|
73
71
|
def boot_server
|
74
72
|
env.socket_path.unlink if env.socket_path.exist?
|
75
73
|
|
76
|
-
pid = Process.spawn(
|
77
|
-
timeout = Time.now +
|
74
|
+
pid = Process.spawn(server_process_env, env.server_command, out: File::NULL)
|
75
|
+
timeout = Time.now + Spring.boot_timeout
|
78
76
|
|
79
77
|
@server_booted = true
|
80
78
|
|
@@ -85,7 +83,7 @@ module Spring
|
|
85
83
|
exit status.exitstatus
|
86
84
|
elsif Time.now > timeout
|
87
85
|
$stderr.puts "Starting Spring server with `#{env.server_command}` " \
|
88
|
-
"timed out after #{
|
86
|
+
"timed out after #{Spring.boot_timeout} seconds"
|
89
87
|
exit 1
|
90
88
|
end
|
91
89
|
|
@@ -107,6 +105,14 @@ module Spring
|
|
107
105
|
}
|
108
106
|
end
|
109
107
|
|
108
|
+
def reset_env
|
109
|
+
ENV.slice(*Spring.reset_on_env)
|
110
|
+
end
|
111
|
+
|
112
|
+
def server_process_env
|
113
|
+
reset_env.merge(gem_env)
|
114
|
+
end
|
115
|
+
|
110
116
|
def stop_server
|
111
117
|
server.close
|
112
118
|
@server = nil
|
@@ -114,9 +120,18 @@ module Spring
|
|
114
120
|
end
|
115
121
|
|
116
122
|
def verify_server_version
|
117
|
-
|
123
|
+
unless IO.select([server], [], [], Spring.connect_timeout)
|
124
|
+
raise "Error connecting to Spring server"
|
125
|
+
end
|
126
|
+
|
127
|
+
line = server.gets
|
128
|
+
unless line
|
129
|
+
raise "Error connecting to Spring server"
|
130
|
+
end
|
131
|
+
|
132
|
+
server_version = line.chomp
|
118
133
|
if server_version != env.version
|
119
|
-
$stderr.puts "There is a version mismatch between the
|
134
|
+
$stderr.puts "There is a version mismatch between the Spring client " \
|
120
135
|
"(#{env.version}) and the server (#{server_version})."
|
121
136
|
|
122
137
|
if server_booted?
|
@@ -132,9 +147,9 @@ module Spring
|
|
132
147
|
|
133
148
|
def connect_to_application(client)
|
134
149
|
server.send_io client
|
135
|
-
send_json server, "args" => args, "default_rails_env" => default_rails_env
|
150
|
+
send_json server, "args" => args, "default_rails_env" => default_rails_env, "spawn_env" => spawn_env, "reset_env" => reset_env
|
136
151
|
|
137
|
-
if IO.select([server], [], [],
|
152
|
+
if IO.select([server], [], [], Spring.connect_timeout)
|
138
153
|
server.gets or raise CommandNotFound
|
139
154
|
else
|
140
155
|
raise "Error connecting to Spring server"
|
@@ -142,12 +157,17 @@ module Spring
|
|
142
157
|
end
|
143
158
|
|
144
159
|
def run_command(client, application)
|
145
|
-
log "sending command"
|
146
|
-
|
147
160
|
application.send_io STDOUT
|
148
161
|
application.send_io STDERR
|
149
162
|
application.send_io STDIN
|
150
163
|
|
164
|
+
log "waiting for the application to be preloaded"
|
165
|
+
preload_status = application.gets
|
166
|
+
preload_status = preload_status.chomp if preload_status
|
167
|
+
log "app preload status: #{preload_status}"
|
168
|
+
exit 1 if preload_status == "1"
|
169
|
+
|
170
|
+
log "sending command"
|
151
171
|
send_json application, "args" => args, "env" => ENV.to_hash
|
152
172
|
|
153
173
|
pid = server.gets
|
@@ -161,6 +181,8 @@ module Spring
|
|
161
181
|
if pid && !pid.empty?
|
162
182
|
log "got pid: #{pid}"
|
163
183
|
|
184
|
+
suspend_resume_on_tstp_cont(pid)
|
185
|
+
|
164
186
|
forward_signals(application)
|
165
187
|
status = application.read.to_i
|
166
188
|
|
@@ -181,6 +203,18 @@ module Spring
|
|
181
203
|
end
|
182
204
|
end
|
183
205
|
|
206
|
+
def suspend_resume_on_tstp_cont(pid)
|
207
|
+
trap("TSTP") {
|
208
|
+
log "suspended"
|
209
|
+
Process.kill("STOP", pid.to_i)
|
210
|
+
Process.kill("STOP", Process.pid)
|
211
|
+
}
|
212
|
+
trap("CONT") {
|
213
|
+
log "resumed"
|
214
|
+
Process.kill("CONT", pid.to_i)
|
215
|
+
}
|
216
|
+
end
|
217
|
+
|
184
218
|
def forward_signals(application)
|
185
219
|
@signal_queue.each { |sig| kill sig, application }
|
186
220
|
|
@@ -213,6 +247,10 @@ module Spring
|
|
213
247
|
def default_rails_env
|
214
248
|
ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
215
249
|
end
|
250
|
+
|
251
|
+
def spawn_env
|
252
|
+
ENV.slice(*Spring.spawn_on_env)
|
253
|
+
end
|
216
254
|
end
|
217
255
|
end
|
218
256
|
end
|
data/lib/spring/client/stop.rb
CHANGED
data/lib/spring/commands.rb
CHANGED
@@ -28,11 +28,18 @@ module Spring
|
|
28
28
|
config = File.expand_path("~/.spring.rb")
|
29
29
|
require config if File.exist?(config)
|
30
30
|
|
31
|
-
#
|
32
|
-
|
33
|
-
|
31
|
+
# We force the TTY so bundler doesn't show a backtrace in case gems are missing.
|
32
|
+
old_env = ENV["BUNDLER_FORCE_TTY"]
|
33
|
+
ENV["BUNDLER_FORCE_TTY"] = "true"
|
34
|
+
begin
|
35
|
+
# If the config/spring.rb contains requires for commands from other gems,
|
36
|
+
# then we need to be under bundler.
|
37
|
+
require "bundler/setup"
|
38
|
+
ensure
|
39
|
+
ENV["BUNDLER_FORCE_TTY"] = old_env
|
40
|
+
end
|
34
41
|
|
35
|
-
# Auto-require any
|
42
|
+
# Auto-require any Spring extensions which are in the Gemfile
|
36
43
|
Gem::Specification.map(&:name).grep(/^spring-/).each do |command|
|
37
44
|
begin
|
38
45
|
require command
|
data/lib/spring/configuration.rb
CHANGED
@@ -1,11 +1,30 @@
|
|
1
1
|
require "spring/errors"
|
2
2
|
|
3
3
|
module Spring
|
4
|
+
@connect_timeout = 5
|
5
|
+
@boot_timeout = 20
|
6
|
+
|
4
7
|
class << self
|
5
|
-
attr_accessor :application_root, :
|
8
|
+
attr_accessor :application_root, :connect_timeout, :boot_timeout
|
9
|
+
attr_writer :quiet
|
6
10
|
|
7
11
|
def gemfile
|
8
|
-
|
12
|
+
require "bundler"
|
13
|
+
|
14
|
+
if /\s1.9.[0-9]/ === Bundler.ruby_scope.gsub(/[\/\s]+/,'')
|
15
|
+
Pathname.new(ENV["BUNDLE_GEMFILE"] || "Gemfile").expand_path
|
16
|
+
else
|
17
|
+
Bundler.default_gemfile
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def gemfile_lock
|
22
|
+
case gemfile.to_s
|
23
|
+
when /\bgems\.rb\z/
|
24
|
+
gemfile.sub_ext('.locked')
|
25
|
+
else
|
26
|
+
gemfile.sub_ext('.lock')
|
27
|
+
end
|
9
28
|
end
|
10
29
|
|
11
30
|
def after_fork_callbacks
|
@@ -16,6 +35,14 @@ module Spring
|
|
16
35
|
after_fork_callbacks << block
|
17
36
|
end
|
18
37
|
|
38
|
+
def spawn_on_env
|
39
|
+
@spawn_on_env ||= []
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset_on_env
|
43
|
+
@reset_on_env ||= []
|
44
|
+
end
|
45
|
+
|
19
46
|
def verify_environment
|
20
47
|
application_root_path
|
21
48
|
end
|
@@ -37,6 +64,10 @@ module Spring
|
|
37
64
|
@project_root_path ||= find_project_root(Pathname.new(File.expand_path(Dir.pwd)))
|
38
65
|
end
|
39
66
|
|
67
|
+
def quiet
|
68
|
+
@quiet || ENV.key?('SPRING_QUIET')
|
69
|
+
end
|
70
|
+
|
40
71
|
private
|
41
72
|
|
42
73
|
def find_project_root(current_dir)
|
data/lib/spring/env.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
require "pathname"
|
2
|
-
require "fileutils"
|
3
|
-
require "digest/md5"
|
4
|
-
require "tmpdir"
|
5
2
|
|
6
3
|
require "spring/version"
|
7
|
-
require "spring/sid"
|
8
4
|
require "spring/configuration"
|
9
5
|
|
10
6
|
module Spring
|
@@ -33,15 +29,18 @@ module Spring
|
|
33
29
|
end
|
34
30
|
|
35
31
|
def tmp_path
|
32
|
+
require "tmpdir"
|
36
33
|
path = Pathname.new(
|
37
34
|
ENV["SPRING_TMP_PATH"] ||
|
38
35
|
File.join(ENV['XDG_RUNTIME_DIR'] || Dir.tmpdir, "spring-#{Process.uid}")
|
39
36
|
)
|
37
|
+
require "fileutils"
|
40
38
|
FileUtils.mkdir_p(path) unless path.exist?
|
41
39
|
path
|
42
40
|
end
|
43
41
|
|
44
42
|
def application_id
|
43
|
+
require "digest/md5"
|
45
44
|
ENV["SPRING_APPLICATION_ID"] || Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)
|
46
45
|
end
|
47
46
|
|
data/lib/spring/errors.rb
CHANGED
@@ -24,7 +24,7 @@ module Spring
|
|
24
24
|
|
25
25
|
def message
|
26
26
|
"Spring was unable to find your config/application.rb file. " \
|
27
|
-
"Your project root was detected at #{project_root}, so
|
27
|
+
"Your project root was detected at #{project_root}, so Spring " \
|
28
28
|
"looked for #{project_root}/config/application.rb but it doesn't exist. You can " \
|
29
29
|
"configure the root of your application by setting Spring.application_root in " \
|
30
30
|
"config/spring.rb."
|
data/lib/spring/json.rb
CHANGED
@@ -46,11 +46,9 @@ end
|
|
46
46
|
|
47
47
|
# See https://github.com/kr/okjson for updates.
|
48
48
|
|
49
|
-
require 'stringio'
|
50
|
-
|
51
49
|
# Some parts adapted from
|
52
|
-
#
|
53
|
-
#
|
50
|
+
# https://golang.org/src/pkg/json/decode.go and
|
51
|
+
# https://golang.org/src/pkg/utf8/utf8.go
|
54
52
|
module Spring
|
55
53
|
module OkJson
|
56
54
|
Upstream = '43'
|
@@ -468,19 +466,18 @@ private
|
|
468
466
|
|
469
467
|
|
470
468
|
def strenc(s)
|
471
|
-
t =
|
472
|
-
t.putc(?")
|
469
|
+
t = '"'.b
|
473
470
|
r = 0
|
474
471
|
|
475
472
|
while r < s.length
|
476
473
|
case s[r]
|
477
|
-
when ?" then t
|
478
|
-
when ?\\ then t
|
479
|
-
when ?\b then t
|
480
|
-
when ?\f then t
|
481
|
-
when ?\n then t
|
482
|
-
when ?\r then t
|
483
|
-
when ?\t then t
|
474
|
+
when ?" then t << '\\"'
|
475
|
+
when ?\\ then t << '\\\\'
|
476
|
+
when ?\b then t << '\\b'
|
477
|
+
when ?\f then t << '\\f'
|
478
|
+
when ?\n then t << '\\n'
|
479
|
+
when ?\r then t << '\\r'
|
480
|
+
when ?\t then t << '\\t'
|
484
481
|
else
|
485
482
|
c = s[r]
|
486
483
|
# In ruby >= 1.9, s[r] is a codepoint, not a byte.
|
@@ -490,14 +487,14 @@ private
|
|
490
487
|
if c.ord < Spc.ord
|
491
488
|
c = "\\u%04x" % [c.ord]
|
492
489
|
end
|
493
|
-
t
|
490
|
+
t << c
|
494
491
|
rescue
|
495
|
-
t
|
492
|
+
t << Ustrerr
|
496
493
|
end
|
497
494
|
elsif c < Spc
|
498
|
-
t
|
495
|
+
t << "\\u%04x" % c
|
499
496
|
elsif Spc <= c && c <= ?~
|
500
|
-
t
|
497
|
+
t << c
|
501
498
|
else
|
502
499
|
n = ucharcopy(t, s, r) # ensure valid UTF-8 output
|
503
500
|
r += n - 1 # r is incremented below
|
@@ -505,8 +502,8 @@ private
|
|
505
502
|
end
|
506
503
|
r += 1
|
507
504
|
end
|
508
|
-
t
|
509
|
-
t
|
505
|
+
t << '"'
|
506
|
+
t
|
510
507
|
end
|
511
508
|
|
512
509
|
|
@@ -531,7 +528,7 @@ private
|
|
531
528
|
|
532
529
|
# 1-byte, 7-bit sequence?
|
533
530
|
if c0 < Utagx
|
534
|
-
t
|
531
|
+
t << c0
|
535
532
|
return 1
|
536
533
|
end
|
537
534
|
|
@@ -544,8 +541,8 @@ private
|
|
544
541
|
# 2-byte, 11-bit sequence?
|
545
542
|
if c0 < Utag3
|
546
543
|
raise Utf8Error if ((c0&Umask2)<<6 | (c1&Umaskx)) <= Uchar1max
|
547
|
-
t
|
548
|
-
t
|
544
|
+
t << c0
|
545
|
+
t << c1
|
549
546
|
return 2
|
550
547
|
end
|
551
548
|
|
@@ -559,9 +556,9 @@ private
|
|
559
556
|
if c0 < Utag4
|
560
557
|
u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx)
|
561
558
|
raise Utf8Error if u <= Uchar2max
|
562
|
-
t
|
563
|
-
t
|
564
|
-
t
|
559
|
+
t << c0
|
560
|
+
t << c1
|
561
|
+
t << c2
|
565
562
|
return 3
|
566
563
|
end
|
567
564
|
|
@@ -574,16 +571,16 @@ private
|
|
574
571
|
if c0 < Utag5
|
575
572
|
u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx)
|
576
573
|
raise Utf8Error if u <= Uchar3max
|
577
|
-
t
|
578
|
-
t
|
579
|
-
t
|
580
|
-
t
|
574
|
+
t << c0
|
575
|
+
t << c1
|
576
|
+
t << c2
|
577
|
+
t << c3
|
581
578
|
return 4
|
582
579
|
end
|
583
580
|
|
584
581
|
raise Utf8Error
|
585
582
|
rescue Utf8Error
|
586
|
-
t
|
583
|
+
t << Ustrerr
|
587
584
|
return 1
|
588
585
|
end
|
589
586
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Spring
|
2
2
|
# Yes, I know this reimplements a bunch of stuff in Active Support, but
|
3
|
-
# I don't want the
|
3
|
+
# I don't want the Spring client to depend on AS, in order to keep its
|
4
4
|
# load time down.
|
5
5
|
class ProcessTitleUpdater
|
6
6
|
SECOND = 1
|
data/lib/spring/server.rb
CHANGED
@@ -19,7 +19,9 @@ module Spring
|
|
19
19
|
def initialize(options = {})
|
20
20
|
@foreground = options.fetch(:foreground, false)
|
21
21
|
@env = options[:env] || default_env
|
22
|
-
@applications = Hash.new
|
22
|
+
@applications = Hash.new do |hash, key|
|
23
|
+
hash[key] = ApplicationManager.new(*key, env)
|
24
|
+
end
|
23
25
|
@pidfile = env.pidfile_path.open('a')
|
24
26
|
@mutex = Mutex.new
|
25
27
|
end
|
@@ -57,12 +59,15 @@ module Spring
|
|
57
59
|
app_client = client.recv_io
|
58
60
|
command = JSON.load(client.read(client.gets.to_i))
|
59
61
|
|
60
|
-
args, default_rails_env = command.values_at('args', 'default_rails_env')
|
62
|
+
args, default_rails_env, spawn_env, reset_env = command.values_at('args', 'default_rails_env', 'spawn_env', 'reset_env')
|
61
63
|
|
62
64
|
if Spring.command?(args.first)
|
65
|
+
application = @applications[rails_env_for(args, default_rails_env, spawn_env)]
|
66
|
+
reset_if_env_changed(application, reset_env)
|
67
|
+
|
63
68
|
log "running command #{args.first}"
|
64
69
|
client.puts
|
65
|
-
client.puts
|
70
|
+
client.puts application.run(app_client)
|
66
71
|
else
|
67
72
|
log "command not found #{args.first}"
|
68
73
|
client.close
|
@@ -73,15 +78,16 @@ module Spring
|
|
73
78
|
redirect_output
|
74
79
|
end
|
75
80
|
|
76
|
-
def rails_env_for(args, default_rails_env)
|
77
|
-
Spring.command(args.first).env(args.drop(1)) || default_rails_env
|
81
|
+
def rails_env_for(args, default_rails_env, spawn_env)
|
82
|
+
[Spring.command(args.first).env(args.drop(1)) || default_rails_env, spawn_env]
|
78
83
|
end
|
79
84
|
|
80
85
|
# Boot the server into the process group of the current session.
|
81
86
|
# This will cause it to be automatically killed once the session
|
82
87
|
# ends (i.e. when the user closes their terminal).
|
83
88
|
def set_pgid
|
84
|
-
Process.
|
89
|
+
pgid = Process.getpgid(Process.getsid)
|
90
|
+
Process.setpgid(0, pgid)
|
85
91
|
end
|
86
92
|
|
87
93
|
# Ignore SIGINT and SIGQUIT otherwise the user typing ^C or ^\ on the command line
|
@@ -135,6 +141,10 @@ module Spring
|
|
135
141
|
|
136
142
|
private
|
137
143
|
|
144
|
+
def reset_if_env_changed(application, reset_env)
|
145
|
+
application.stop if ENV.slice(*reset_env.keys) != reset_env
|
146
|
+
end
|
147
|
+
|
138
148
|
def default_env
|
139
149
|
Env.new(log_file: default_log_file)
|
140
150
|
end
|
data/lib/spring/version.rb
CHANGED