spring 2.0.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -44,7 +44,7 @@ module Spring
44
44
  require "spring/commands"
45
45
 
46
46
  if Spring.command?(args.first)
47
- # Command installed since spring started
47
+ # Command installed since Spring started
48
48
  stop_server
49
49
  cold_run
50
50
  else
@@ -116,7 +116,7 @@ module Spring
116
116
  def verify_server_version
117
117
  server_version = server.gets.chomp
118
118
  if server_version != env.version
119
- $stderr.puts "There is a version mismatch between the spring client " \
119
+ $stderr.puts "There is a version mismatch between the Spring client " \
120
120
  "(#{env.version}) and the server (#{server_version})."
121
121
 
122
122
  if server_booted?
@@ -161,6 +161,8 @@ module Spring
161
161
  if pid && !pid.empty?
162
162
  log "got pid: #{pid}"
163
163
 
164
+ suspend_resume_on_tstp_cont(pid)
165
+
164
166
  forward_signals(application)
165
167
  status = application.read.to_i
166
168
 
@@ -181,6 +183,18 @@ module Spring
181
183
  end
182
184
  end
183
185
 
186
+ def suspend_resume_on_tstp_cont(pid)
187
+ trap("TSTP") {
188
+ log "suspended"
189
+ Process.kill("STOP", pid.to_i)
190
+ Process.kill("STOP", Process.pid)
191
+ }
192
+ trap("CONT") {
193
+ log "resumed"
194
+ Process.kill("CONT", pid.to_i)
195
+ }
196
+ end
197
+
184
198
  def forward_signals(application)
185
199
  @signal_queue.each { |sig| kill sig, application }
186
200
 
@@ -4,7 +4,7 @@ module Spring
4
4
  module Client
5
5
  class Stop < Command
6
6
  def self.description
7
- "Stop all spring processes for this project."
7
+ "Stop all Spring processes for this project."
8
8
  end
9
9
 
10
10
  def call
@@ -32,7 +32,7 @@ module Spring
32
32
  # then we need to be under bundler.
33
33
  require "bundler/setup"
34
34
 
35
- # Auto-require any spring extensions which are in the Gemfile
35
+ # Auto-require any Spring extensions which are in the Gemfile
36
36
  Gem::Specification.map(&:name).grep(/^spring-/).each do |command|
37
37
  begin
38
38
  require command
@@ -5,7 +5,11 @@ module Spring
5
5
  attr_accessor :application_root, :quiet
6
6
 
7
7
  def gemfile
8
- ENV['BUNDLE_GEMFILE'] || "Gemfile"
8
+ if /\s1.9.[0-9]/ === Bundler.ruby_scope.gsub(/[\/\s]+/,'')
9
+ ENV["BUNDLE_GEMFILE"] || "Gemfile"
10
+ else
11
+ Bundler.default_gemfile
12
+ end
9
13
  end
10
14
 
11
15
  def after_fork_callbacks
data/lib/spring/env.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  require "pathname"
2
- require "fileutils"
3
2
  require "digest/md5"
4
- require "tmpdir"
5
3
 
6
4
  require "spring/version"
7
5
  require "spring/sid"
@@ -33,10 +31,12 @@ module Spring
33
31
  end
34
32
 
35
33
  def tmp_path
34
+ require "tmpdir"
36
35
  path = Pathname.new(
37
36
  ENV["SPRING_TMP_PATH"] ||
38
37
  File.join(ENV['XDG_RUNTIME_DIR'] || Dir.tmpdir, "spring-#{Process.uid}")
39
38
  )
39
+ require "fileutils"
40
40
  FileUtils.mkdir_p(path) unless path.exist?
41
41
  path
42
42
  end
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 spring " \
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."
@@ -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 spring client to depend on AS, in order to keep its
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
@@ -1,3 +1,3 @@
1
1
  module Spring
2
- VERSION = "2.0.1"
2
+ VERSION = "3.0.0"
3
3
  end
@@ -23,9 +23,21 @@ module Spring
23
23
  @directories = Set.new
24
24
  @stale = false
25
25
  @listeners = []
26
+
27
+ @on_debug = nil
28
+ end
29
+
30
+ def on_debug(&block)
31
+ @on_debug = block
32
+ end
33
+
34
+ def debug
35
+ @on_debug.call(yield) if @on_debug
26
36
  end
27
37
 
28
38
  def add(*items)
39
+ debug { "watcher: add: #{items.inspect}" }
40
+
29
41
  items = items.flatten.map do |item|
30
42
  item = Pathname.new(item)
31
43
 
@@ -36,14 +48,30 @@ module Spring
36
48
  end
37
49
  end
38
50
 
39
- items = items.select(&:exist?)
51
+ items = items.select do |item|
52
+ if item.symlink?
53
+ item.readlink.exist?.tap do |exists|
54
+ if !exists
55
+ debug { "add: ignoring dangling symlink: #{item.inspect} -> #{item.readlink.inspect}" }
56
+ end
57
+ end
58
+ else
59
+ item.exist?
60
+ end
61
+ end
40
62
 
41
63
  synchronize {
42
64
  items.each do |item|
43
65
  if item.directory?
44
66
  directories << item.realpath.to_s
45
67
  else
46
- files << item.realpath.to_s
68
+ begin
69
+ files << item.realpath.to_s
70
+ rescue Errno::ENOENT
71
+ # Race condition. Ignore symlinks whose target was removed
72
+ # since the check above, or are deeply chained.
73
+ debug { "add: ignoring now-dangling symlink: #{item.inspect} -> #{item.readlink.inspect}" }
74
+ end
47
75
  end
48
76
  end
49
77
 
@@ -56,16 +84,19 @@ module Spring
56
84
  end
57
85
 
58
86
  def on_stale(&block)
87
+ debug { "added listener: #{block.inspect}" }
59
88
  @listeners << block
60
89
  end
61
90
 
62
91
  def mark_stale
63
92
  return if stale?
64
93
  @stale = true
94
+ debug { "marked stale, calling listeners: listeners=#{@listeners.inspect}" }
65
95
  @listeners.each(&:call)
66
96
  end
67
97
 
68
98
  def restart
99
+ debug { "restarting" }
69
100
  stop
70
101
  start
71
102
  end
@@ -12,7 +12,13 @@ module Spring
12
12
  end
13
13
 
14
14
  def check_stale
15
- synchronize { mark_stale if mtime < compute_mtime }
15
+ synchronize do
16
+ computed = compute_mtime
17
+ if mtime < computed
18
+ debug { "check_stale: mtime=#{mtime.inspect} < computed=#{computed.inspect}" }
19
+ mark_stale
20
+ end
21
+ end
16
22
  end
17
23
 
18
24
  def add(*)
@@ -21,36 +27,67 @@ module Spring
21
27
  end
22
28
 
23
29
  def start
30
+ debug { "start: poller=#{@poller.inspect}" }
24
31
  unless @poller
25
32
  @poller = Thread.new {
26
33
  Thread.current.abort_on_exception = true
27
34
 
28
- loop do
29
- Kernel.sleep latency
30
- check_stale
35
+ begin
36
+ until stale?
37
+ Kernel.sleep latency
38
+ check_stale
39
+ end
40
+ rescue Exception => e
41
+ debug do
42
+ "poller: aborted: #{e.class}: #{e}\n #{e.backtrace.join("\n ")}"
43
+ end
44
+ raise
45
+ ensure
46
+ @poller = nil
31
47
  end
32
48
  }
33
49
  end
34
50
  end
35
51
 
36
52
  def stop
53
+ debug { "stopping poller: #{@poller.inspect}" }
37
54
  if @poller
38
55
  @poller.kill
39
56
  @poller = nil
40
57
  end
41
58
  end
42
59
 
60
+ def running?
61
+ @poller && @poller.alive?
62
+ end
63
+
43
64
  def subjects_changed
44
- @mtime = compute_mtime
65
+ computed = compute_mtime
66
+ debug { "subjects_changed: mtime #{@mtime} -> #{computed}" }
67
+ @mtime = computed
45
68
  end
46
69
 
47
70
  private
48
71
 
49
72
  def compute_mtime
50
- expanded_files.map { |f| File.mtime(f).to_f }.max || 0
51
- rescue Errno::ENOENT
52
- # if a file does no longer exist, the watcher is always stale.
53
- Float::MAX
73
+ expanded_files.map do |f|
74
+ # Get the mtime of symlink targets. Ignore dangling symlinks.
75
+ if File.symlink?(f)
76
+ begin
77
+ File.mtime(f)
78
+ rescue Errno::ENOENT
79
+ 0
80
+ end
81
+ # If a file no longer exists, treat it as changed.
82
+ else
83
+ begin
84
+ File.mtime(f)
85
+ rescue Errno::ENOENT
86
+ debug { "compute_mtime: no longer exists: #{f}" }
87
+ Float::MAX
88
+ end
89
+ end.to_f
90
+ end.max || 0
54
91
  end
55
92
 
56
93
  def expanded_files
metadata CHANGED
@@ -1,31 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spring
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Leighton
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-21 00:00:00.000000000 Z
11
+ date: 2021-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activesupport
14
+ name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.2'
20
- type: :runtime
19
+ version: '0'
20
+ type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4.2'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: bump
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: bump
42
+ name: activesupport
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -91,12 +91,6 @@ files:
91
91
  - lib/spring/process_title_updater.rb
92
92
  - lib/spring/server.rb
93
93
  - lib/spring/sid.rb
94
- - lib/spring/test.rb
95
- - lib/spring/test/acceptance_test.rb
96
- - lib/spring/test/application.rb
97
- - lib/spring/test/application_generator.rb
98
- - lib/spring/test/rails_version.rb
99
- - lib/spring/test/watcher_test.rb
100
94
  - lib/spring/version.rb
101
95
  - lib/spring/watcher.rb
102
96
  - lib/spring/watcher/abstract.rb
@@ -105,7 +99,7 @@ homepage: https://github.com/rails/spring
105
99
  licenses:
106
100
  - MIT
107
101
  metadata: {}
108
- post_install_message:
102
+ post_install_message:
109
103
  rdoc_options: []
110
104
  require_paths:
111
105
  - lib
@@ -113,16 +107,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
113
107
  requirements:
114
108
  - - ">="
115
109
  - !ruby/object:Gem::Version
116
- version: '0'
110
+ version: 2.5.0
117
111
  required_rubygems_version: !ruby/object:Gem::Requirement
118
112
  requirements:
119
113
  - - ">="
120
114
  - !ruby/object:Gem::Version
121
115
  version: '0'
122
116
  requirements: []
123
- rubyforge_project:
124
- rubygems_version: 2.5.1
125
- signing_key:
117
+ rubygems_version: 3.2.22
118
+ signing_key:
126
119
  specification_version: 4
127
120
  summary: Rails application preloader
128
121
  test_files: []
@@ -1,558 +0,0 @@
1
- require "io/wait"
2
- require "timeout"
3
- require "spring/sid"
4
- require "spring/client"
5
- require "active_support/core_ext/string/strip"
6
-
7
- module Spring
8
- module Test
9
- class AcceptanceTest < ActiveSupport::TestCase
10
- runnables.delete self # prevent Minitest running this class
11
-
12
- DEFAULT_SPEEDUP = 0.8
13
-
14
- def rails_version
15
- ENV['RAILS_VERSION'] || '~> 5.0.0'
16
- end
17
-
18
- # Extension point for spring-watchers-listen
19
- def generator_klass
20
- Spring::Test::ApplicationGenerator
21
- end
22
-
23
- def generator
24
- @@generator ||= generator_klass.new(rails_version)
25
- end
26
-
27
- def app
28
- @app ||= Spring::Test::Application.new("#{Spring::Test.root}/apps/tmp")
29
- end
30
-
31
- def spring_env
32
- app.spring_env
33
- end
34
-
35
- def assert_output(artifacts, expected)
36
- expected.each do |stream, output|
37
- assert artifacts[stream].include?(output),
38
- "expected #{stream} to include '#{output}'.\n\n#{app.debug(artifacts)}"
39
- end
40
- end
41
-
42
- def assert_success(command, expected_output = nil)
43
- artifacts = app.run(*Array(command))
44
- assert artifacts[:status].success?, "expected successful exit status\n\n#{app.debug(artifacts)}"
45
- assert_output artifacts, expected_output if expected_output
46
- end
47
-
48
- def assert_failure(command, expected_output = nil)
49
- artifacts = app.run(*Array(command))
50
- assert !artifacts[:status].success?, "expected unsuccessful exit status\n\n#{app.debug(artifacts)}"
51
- assert_output artifacts, expected_output if expected_output
52
- end
53
-
54
- def refute_output_includes(command, not_expected)
55
- artifacts = app.run(*Array(command))
56
- not_expected.each do |stream, output|
57
- assert !artifacts[stream].include?(output),
58
- "expected #{stream} to not include '#{output}'.\n\n#{app.debug(artifacts)}"
59
- end
60
- end
61
-
62
- def assert_speedup(ratio = DEFAULT_SPEEDUP)
63
- if ENV['CI']
64
- yield
65
- else
66
- app.with_timing do
67
- yield
68
- assert app.timing_ratio < ratio, "#{app.last_time} was not less than #{ratio} of #{app.first_time}"
69
- end
70
- end
71
- end
72
-
73
- def without_gem(name)
74
- gem_home = app.gem_home.join('gems')
75
- FileUtils.mv(gem_home.join(name), app.root)
76
- yield
77
- ensure
78
- FileUtils.mv(app.root.join(name), gem_home)
79
- end
80
-
81
- setup do
82
- generator.generate_if_missing
83
- generator.install_spring
84
- generator.copy_to(app.root)
85
- end
86
-
87
- teardown do
88
- app.stop_spring
89
- end
90
-
91
- test "basic" do
92
- assert_speedup do
93
- 2.times { app.run app.spring_test_command }
94
- end
95
- end
96
-
97
- test "help message when called without arguments" do
98
- assert_success "bin/spring", stdout: 'Usage: spring COMMAND [ARGS]'
99
- assert spring_env.server_running?
100
- end
101
-
102
- test "shows help" do
103
- assert_success "bin/spring help", stdout: 'Usage: spring COMMAND [ARGS]'
104
- assert_success "bin/spring -h", stdout: 'Usage: spring COMMAND [ARGS]'
105
- assert_success "bin/spring --help", stdout: 'Usage: spring COMMAND [ARGS]'
106
- refute spring_env.server_running?
107
- end
108
-
109
- test "tells the user that spring is being used when used automatically via binstubs" do
110
- assert_success "bin/rails runner ''", stderr: "Running via Spring preloader in process"
111
- assert_success app.spring_test_command, stderr: "Running via Spring preloader in process"
112
- end
113
-
114
- test "does not tell the user that spring is being used when used automatically via binstubs but quiet is enabled" do
115
- File.write("#{app.user_home}/.spring.rb", "Spring.quiet = true")
116
- assert_success "bin/rails runner ''"
117
- refute_output_includes "bin/rails runner ''", stderr: 'Running via Spring preloader in process'
118
- end
119
-
120
- test "test changes are picked up" do
121
- assert_speedup do
122
- assert_success app.spring_test_command, stdout: "0 failures"
123
-
124
- app.insert_into_test "raise 'omg'"
125
- assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
126
- end
127
- end
128
-
129
- test "code changes are picked up" do
130
- assert_speedup do
131
- assert_success app.spring_test_command, stdout: "0 failures"
132
-
133
- File.write(app.controller, app.controller.read.sub("@posts = Post.all", "raise 'omg'"))
134
- assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
135
- end
136
- end
137
-
138
- test "code changes in pre-referenced app files are picked up" do
139
- File.write(app.path("config/initializers/load_posts_controller.rb"), "PostsController\n")
140
-
141
- assert_speedup do
142
- assert_success app.spring_test_command, stdout: "0 failures"
143
-
144
- File.write(app.controller, app.controller.read.sub("@posts = Post.all", "raise 'omg'"))
145
- assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
146
- end
147
- end
148
-
149
- test "app gets reloaded when preloaded files change" do
150
- assert_success app.spring_test_command
151
-
152
- File.write(app.application_config, app.application_config.read + <<-RUBY.strip_heredoc)
153
- class Foo
154
- def self.omg
155
- raise "omg"
156
- end
157
- end
158
- RUBY
159
- app.insert_into_test "Foo.omg"
160
-
161
- app.await_reload
162
- assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
163
- end
164
-
165
- test "app gets reloaded even with a ton of boot output" do
166
- limit = UNIXSocket.pair.first.getsockopt(:SOCKET, :SNDBUF).int
167
-
168
- assert_success app.spring_test_command
169
- File.write(app.path("config/initializers/verbose.rb"), "#{limit}.times { puts 'x' }")
170
-
171
- app.await_reload
172
- assert_success app.spring_test_command
173
- end
174
-
175
- test "app gets reloaded even with abort_on_exception=true" do
176
- assert_success app.spring_test_command
177
- File.write(app.path("config/initializers/thread_config.rb"), "Thread.abort_on_exception = true")
178
-
179
- app.await_reload
180
- assert_success app.spring_test_command
181
- end
182
-
183
- test "app recovers when a boot-level error is introduced" do
184
- config = app.application_config.read
185
-
186
- assert_success app.spring_test_command
187
-
188
- File.write(app.application_config, "#{config}\nomg")
189
- app.await_reload
190
-
191
- assert_failure app.spring_test_command
192
-
193
- File.write(app.application_config, config)
194
- assert_success app.spring_test_command
195
- end
196
-
197
- test "stop command kills server" do
198
- app.run app.spring_test_command
199
- assert spring_env.server_running?, "The server should be running but it isn't"
200
-
201
- assert_success "bin/spring stop"
202
- assert !spring_env.server_running?, "The server should not be running but it is"
203
- end
204
-
205
- test "custom commands" do
206
- # Start spring before setting up the command, to test that it gracefully upgrades itself
207
- assert_success "bin/rails runner ''"
208
-
209
- File.write(app.spring_config, <<-RUBY.strip_heredoc)
210
- class CustomCommand
211
- def call
212
- puts "omg"
213
- end
214
-
215
- def exec_name
216
- "rake"
217
- end
218
- end
219
-
220
- Spring.register_command "custom", CustomCommand.new
221
- RUBY
222
-
223
- assert_success "bin/spring custom", stdout: "omg"
224
-
225
- assert_success "bin/spring binstub custom"
226
- assert_success "bin/custom", stdout: "omg"
227
-
228
- app.env["DISABLE_SPRING"] = "1"
229
- assert_success %{bin/custom -e 'puts "foo"'}, stdout: "foo"
230
- end
231
-
232
- test "binstub" do
233
- assert_success "bin/rails server --help", stdout: "Usage: rails server" # rails command fallback
234
-
235
- assert_success "#{app.spring} binstub rake", stdout: "bin/rake: spring already present"
236
-
237
- assert_success "#{app.spring} binstub --remove rake", stdout: "bin/rake: spring removed"
238
- assert !app.path("bin/rake").read.include?(Spring::Client::Binstub::LOADER)
239
- assert_success "bin/rake -T", stdout: "rake db:migrate"
240
- end
241
-
242
- test "binstub remove all" do
243
- assert_success "bin/spring binstub --remove --all"
244
- refute File.exist?(app.path("bin/spring"))
245
- end
246
-
247
- test "binstub when spring gem is missing" do
248
- without_gem "spring-#{Spring::VERSION}" do
249
- File.write(app.gemfile, app.gemfile.read.gsub(/gem 'spring.*/, ""))
250
- assert_success "bin/rake -T", stdout: "rake db:migrate"
251
- end
252
- end
253
-
254
- test "binstub when spring binary is missing" do
255
- begin
256
- File.rename(app.path("bin/spring"), app.path("bin/spring.bak"))
257
- assert_success "bin/rake -T", stdout: "rake db:migrate"
258
- ensure
259
- File.rename(app.path("bin/spring.bak"), app.path("bin/spring"))
260
- end
261
- end
262
-
263
- test "binstub upgrade with old binstub" do
264
- File.write(app.path("bin/rake"), <<-RUBY.strip_heredoc)
265
- #!/usr/bin/env ruby
266
-
267
- if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?
268
- exec "bundle", "exec", "rake", *ARGV
269
- else
270
- ARGV.unshift "rake"
271
- load Gem.bin_path("spring", "spring")
272
- end
273
- RUBY
274
-
275
- File.write(app.path("bin/rails"), <<-RUBY.strip_heredoc)
276
- #!/usr/bin/env ruby
277
-
278
- if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?
279
- APP_PATH = File.expand_path('../../config/application', __FILE__)
280
- require_relative '../config/boot'
281
- require 'rails/commands'
282
- else
283
- ARGV.unshift "rails"
284
- load Gem.bin_path("spring", "spring")
285
- end
286
- RUBY
287
-
288
- assert_success "bin/spring binstub --all", stdout: "upgraded"
289
-
290
- expected = <<-RUBY.gsub(/^ /, "")
291
- #!/usr/bin/env ruby
292
- #{Spring::Client::Binstub::LOADER.strip}
293
- require 'bundler/setup'
294
- load Gem.bin_path('rake', 'rake')
295
- RUBY
296
- assert_equal expected, app.path("bin/rake").read
297
-
298
- expected = <<-RUBY.gsub(/^ /, "")
299
- #!/usr/bin/env ruby
300
- #{Spring::Client::Binstub::LOADER.strip}
301
- APP_PATH = File.expand_path('../../config/application', __FILE__)
302
- require_relative '../config/boot'
303
- require 'rails/commands'
304
- RUBY
305
- assert_equal expected, app.path("bin/rails").read
306
- end
307
-
308
- test "binstub upgrade with new binstub variations" do
309
- expected = <<-RUBY.gsub(/^ /, "")
310
- #!/usr/bin/env ruby
311
- #{Spring::Client::Binstub::LOADER.strip}
312
- require 'bundler/setup'
313
- load Gem.bin_path('rake', 'rake')
314
- RUBY
315
-
316
- # older variation with double quotes
317
- File.write(app.path("bin/rake"), <<-RUBY.strip_heredoc)
318
- #!/usr/bin/env ruby
319
- begin
320
- load File.expand_path("../spring", __FILE__)
321
- rescue LoadError
322
- end
323
- require 'bundler/setup'
324
- load Gem.bin_path('rake', 'rake')
325
- RUBY
326
-
327
- assert_success "bin/spring binstub rake", stdout: "bin/rake: upgraded"
328
- assert_equal expected, app.path("bin/rake").read
329
-
330
- # newer variation with single quotes
331
- File.write(app.path("bin/rake"), <<-RUBY.strip_heredoc)
332
- #!/usr/bin/env ruby
333
- begin
334
- load File.expand_path('../spring', __FILE__)
335
- rescue LoadError
336
- end
337
- require 'bundler/setup'
338
- load Gem.bin_path('rake', 'rake')
339
- RUBY
340
-
341
- assert_success "bin/spring binstub rake", stdout: "bin/rake: upgraded"
342
- assert_equal expected, app.path("bin/rake").read
343
-
344
- # newer variation which checks end of exception message
345
- File.write(app.path("bin/rake"), <<-RUBY.strip_heredoc)
346
- #!/usr/bin/env ruby
347
- begin
348
- spring_bin_path = File.expand_path('../spring', __FILE__)
349
- load spring_bin_path
350
- rescue LoadError => e
351
- raise unless e.message.end_with? spring_bin_path, 'spring/binstub'
352
- end
353
- require 'bundler/setup'
354
- load Gem.bin_path('rake', 'rake')
355
- RUBY
356
-
357
- assert_success "bin/spring binstub rake", stdout: "bin/rake: upgraded"
358
- assert_equal expected, app.path("bin/rake").read
359
- end
360
-
361
- test "binstub remove with new binstub variations" do
362
- # older variation with double quotes
363
- File.write(app.path("bin/rake"), <<-RUBY.strip_heredoc)
364
- #!/usr/bin/env ruby
365
- begin
366
- load File.expand_path("../spring", __FILE__)
367
- rescue LoadError
368
- end
369
- require 'bundler/setup'
370
- load Gem.bin_path('rake', 'rake')
371
- RUBY
372
-
373
- # newer variation with single quotes
374
- File.write(app.path("bin/rails"), <<-RUBY.strip_heredoc)
375
- #!/usr/bin/env ruby
376
- begin
377
- load File.expand_path('../spring', __FILE__)
378
- rescue LoadError
379
- end
380
- APP_PATH = File.expand_path('../../config/application', __FILE__)
381
- require_relative '../config/boot'
382
- require 'rails/commands'
383
- RUBY
384
-
385
- assert_success "bin/spring binstub --remove rake", stdout: "bin/rake: spring removed"
386
- assert_success "bin/spring binstub --remove rails", stdout: "bin/rails: spring removed"
387
-
388
- expected = <<-RUBY.strip_heredoc
389
- #!/usr/bin/env ruby
390
- require 'bundler/setup'
391
- load Gem.bin_path('rake', 'rake')
392
- RUBY
393
- assert_equal expected, app.path("bin/rake").read
394
-
395
- expected = <<-RUBY.strip_heredoc
396
- #!/usr/bin/env ruby
397
- APP_PATH = File.expand_path('../../config/application', __FILE__)
398
- require_relative '../config/boot'
399
- require 'rails/commands'
400
- RUBY
401
- assert_equal expected, app.path("bin/rails").read
402
- end
403
-
404
- test "after fork callback" do
405
- File.write(app.spring_config, "Spring.after_fork { puts '!callback!' }")
406
- assert_success "bin/rails runner 'puts 2'", stdout: "!callback!\n2"
407
- end
408
-
409
- test "global config file evaluated" do
410
- File.write("#{app.user_home}/.spring.rb", "Spring.after_fork { puts '!callback!' }")
411
- assert_success "bin/rails runner 'puts 2'", stdout: "!callback!\n2"
412
- end
413
-
414
- test "can define client tasks" do
415
- File.write("#{app.spring_client_config}", <<-RUBY)
416
- Spring::Client::COMMANDS["foo"] = lambda { |args| puts "bar -- \#{args.inspect}" }
417
- RUBY
418
- assert_success "bin/spring foo --baz", stdout: "bar -- [\"foo\", \"--baz\"]\n"
419
- end
420
-
421
- test "missing config/application.rb" do
422
- app.application_config.delete
423
- assert_failure "bin/rake -T", stderr: "unable to find your config/application.rb"
424
- end
425
-
426
- test "piping with boot-level error" do
427
- config = app.application_config.read
428
- File.write(app.application_config, "#{config}\nomg")
429
- assert_success "bin/rake -T | cat"
430
- end
431
-
432
- test "piping" do
433
- assert_success "bin/rake -T | grep db", stdout: "rake db:migrate"
434
- end
435
-
436
- test "status" do
437
- assert_success "bin/spring status", stdout: "Spring is not running"
438
- assert_success "bin/rails runner ''"
439
- assert_success "bin/spring status", stdout: "Spring is running"
440
- end
441
-
442
- test "runner command sets Rails environment from command-line options" do
443
- assert_success "bin/rails runner -e test 'puts Rails.env'", stdout: "test"
444
- assert_success "bin/rails runner --environment=test 'puts Rails.env'", stdout: "test"
445
- end
446
-
447
- test "forcing rails env via environment variable" do
448
- app.env['RAILS_ENV'] = 'test'
449
- assert_success "bin/rake -p 'Rails.env'", stdout: "test"
450
- end
451
-
452
- test "setting env vars with rake" do
453
- File.write(app.path("lib/tasks/env.rake"), <<-RUBY.strip_heredoc)
454
- task :print_rails_env => :environment do
455
- puts Rails.env
456
- end
457
-
458
- task :print_env do
459
- ENV.each { |k, v| puts "\#{k}=\#{v}" }
460
- end
461
-
462
- task(:default).clear.enhance [:print_rails_env]
463
- RUBY
464
-
465
- assert_success "bin/rake RAILS_ENV=test print_rails_env", stdout: "test"
466
- assert_success "bin/rake FOO=bar print_env", stdout: "FOO=bar"
467
- assert_success "bin/rake", stdout: "test"
468
- end
469
-
470
- test "changing the Gemfile works" do
471
- assert_success %(bin/rails runner 'require "sqlite3"')
472
-
473
- File.write(app.gemfile, app.gemfile.read.sub(%{gem 'sqlite3'}, %{# gem 'sqlite3'}))
474
- app.await_reload
475
-
476
- assert_failure %(bin/rails runner 'require "sqlite3"'), stderr: "sqlite3"
477
- end
478
-
479
- test "changing the Gemfile works when spring calls into itself" do
480
- File.write(app.path("script.rb"), <<-RUBY.strip_heredoc)
481
- gemfile = Rails.root.join("Gemfile")
482
- File.write(gemfile, "\#{gemfile.read}gem 'text'\\n")
483
- Bundler.with_clean_env do
484
- system(#{app.env.inspect}, "bundle install")
485
- end
486
- output = `\#{Rails.root.join('bin/rails')} runner 'require "text"; puts "done";'`
487
- exit output.include? "done\n"
488
- RUBY
489
-
490
- assert_success [%(bin/rails runner 'load Rails.root.join("script.rb")'), timeout: 60]
491
- end
492
-
493
- test "changing the environment between runs" do
494
- File.write(app.application_config, "#{app.application_config.read}\nENV['BAR'] = 'bar'")
495
-
496
- app.env["OMG"] = "1"
497
- app.env["FOO"] = "1"
498
- app.env["RUBYOPT"] = "-rubygems"
499
-
500
- assert_success %(bin/rails runner 'p ENV["OMG"]'), stdout: "1"
501
- assert_success %(bin/rails runner 'p ENV["BAR"]'), stdout: "bar"
502
- assert_success %(bin/rails runner 'p ENV.key?("BUNDLE_GEMFILE")'), stdout: "true"
503
- assert_success %(bin/rails runner 'p ENV["RUBYOPT"]'), stdout: "bundler"
504
-
505
- app.env["OMG"] = "2"
506
- app.env.delete "FOO"
507
-
508
- assert_success %(bin/rails runner 'p ENV["OMG"]'), stdout: "2"
509
- assert_success %(bin/rails runner 'p ENV.key?("FOO")'), stdout: "false"
510
- end
511
-
512
- test "Kernel.raise remains private" do
513
- expr = "p Kernel.private_instance_methods.include?(:raise)"
514
- assert_success %(bin/rails runner '#{expr}'), stdout: "true"
515
- end
516
-
517
- test "custom bundle path" do
518
- bundle_path = app.path(".bundle/#{Bundler.ruby_scope}")
519
- bundle_path.dirname.mkpath
520
-
521
- FileUtils.cp_r "#{app.gem_home}/", bundle_path.to_s
522
-
523
- app.run! "bundle install --path .bundle --clean --local"
524
-
525
- assert_speedup do
526
- 2.times { assert_success "bundle exec rails runner ''" }
527
- end
528
- end
529
-
530
- test "booting a foreground server" do
531
- FileUtils.cd(app.root) do
532
- assert !spring_env.server_running?
533
- assert_success "bin/spring server &"
534
-
535
- Timeout.timeout(10) do
536
- sleep 0.1 until spring_env.server_running? && spring_env.socket_path.exist?
537
- end
538
-
539
- assert_success app.spring_test_command
540
- end
541
- end
542
-
543
- test "server boot timeout" do
544
- app.env["SPRING_SERVER_COMMAND"] = "sleep 1"
545
- File.write("#{app.spring_client_config}", %(
546
- Spring::Client::Run.const_set(:BOOT_TIMEOUT, 0.1)
547
- ))
548
-
549
- assert_failure "bin/rails runner ''", stderr: "timed out"
550
- end
551
-
552
- test "no warnings are shown for unsprung commands" do
553
- app.env["DISABLE_SPRING"] = "1"
554
- refute_output_includes "bin/rails runner ''", stderr: "WARN"
555
- end
556
- end
557
- end
558
- end