spring 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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