spring-jruby 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +364 -0
  4. data/bin/spring +49 -0
  5. data/lib/spring-jruby/application.rb +283 -0
  6. data/lib/spring-jruby/application/boot.rb +19 -0
  7. data/lib/spring-jruby/binstub.rb +13 -0
  8. data/lib/spring-jruby/boot.rb +9 -0
  9. data/lib/spring-jruby/client.rb +46 -0
  10. data/lib/spring-jruby/client/binstub.rb +188 -0
  11. data/lib/spring-jruby/client/command.rb +18 -0
  12. data/lib/spring-jruby/client/help.rb +62 -0
  13. data/lib/spring-jruby/client/rails.rb +34 -0
  14. data/lib/spring-jruby/client/run.rb +167 -0
  15. data/lib/spring-jruby/client/status.rb +30 -0
  16. data/lib/spring-jruby/client/stop.rb +22 -0
  17. data/lib/spring-jruby/client/version.rb +11 -0
  18. data/lib/spring-jruby/command_wrapper.rb +82 -0
  19. data/lib/spring-jruby/commands.rb +51 -0
  20. data/lib/spring-jruby/commands/rails.rb +112 -0
  21. data/lib/spring-jruby/commands/rake.rb +30 -0
  22. data/lib/spring-jruby/configuration.rb +60 -0
  23. data/lib/spring-jruby/env.rb +109 -0
  24. data/lib/spring-jruby/errors.rb +36 -0
  25. data/lib/spring-jruby/impl/application.rb +7 -0
  26. data/lib/spring-jruby/impl/application_manager.rb +7 -0
  27. data/lib/spring-jruby/impl/fork/application.rb +69 -0
  28. data/lib/spring-jruby/impl/fork/application_manager.rb +137 -0
  29. data/lib/spring-jruby/impl/fork/run.rb +47 -0
  30. data/lib/spring-jruby/impl/pool/application.rb +47 -0
  31. data/lib/spring-jruby/impl/pool/application_manager.rb +226 -0
  32. data/lib/spring-jruby/impl/pool/run.rb +27 -0
  33. data/lib/spring-jruby/impl/run.rb +7 -0
  34. data/lib/spring-jruby/io_helpers.rb +92 -0
  35. data/lib/spring-jruby/json.rb +626 -0
  36. data/lib/spring-jruby/platform.rb +23 -0
  37. data/lib/spring-jruby/process_title_updater.rb +65 -0
  38. data/lib/spring-jruby/server.rb +130 -0
  39. data/lib/spring-jruby/sid.rb +42 -0
  40. data/lib/spring-jruby/test.rb +18 -0
  41. data/lib/spring-jruby/test/acceptance_test.rb +371 -0
  42. data/lib/spring-jruby/test/application.rb +217 -0
  43. data/lib/spring-jruby/test/application_generator.rb +134 -0
  44. data/lib/spring-jruby/test/rails_version.rb +40 -0
  45. data/lib/spring-jruby/test/watcher_test.rb +167 -0
  46. data/lib/spring-jruby/version.rb +3 -0
  47. data/lib/spring-jruby/watcher.rb +30 -0
  48. data/lib/spring-jruby/watcher/abstract.rb +86 -0
  49. data/lib/spring-jruby/watcher/polling.rb +61 -0
  50. metadata +137 -0
@@ -0,0 +1,23 @@
1
+ module Spring
2
+ def self.fork?
3
+ Process.respond_to?(:fork)
4
+ end
5
+
6
+ def self.jruby?
7
+ RUBY_PLATFORM == "java"
8
+ end
9
+
10
+ def self.ruby_bin
11
+ if RUBY_PLATFORM == "java"
12
+ "jruby"
13
+ else
14
+ "ruby"
15
+ end
16
+ end
17
+
18
+ if jruby?
19
+ IGNORE_SIGNALS = %w(INT)
20
+ else
21
+ IGNORE_SIGNALS = %w(INT QUIT)
22
+ end
23
+ end
@@ -0,0 +1,65 @@
1
+ module Spring
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
4
+ # load time down.
5
+ class ProcessTitleUpdater
6
+ SECOND = 1
7
+ MINUTE = 60
8
+ HOUR = 60*60
9
+
10
+ def self.run(&block)
11
+ updater = new(&block)
12
+
13
+ Thread.new {
14
+ $0 = updater.value
15
+ loop { $0 = updater.next }
16
+ }
17
+ end
18
+
19
+ attr_reader :block
20
+
21
+ def initialize(start = Time.now, &block)
22
+ @start = start
23
+ @block = block
24
+ end
25
+
26
+ def interval
27
+ distance = Time.now - @start
28
+
29
+ if distance < MINUTE
30
+ SECOND
31
+ elsif distance < HOUR
32
+ MINUTE
33
+ else
34
+ HOUR
35
+ end
36
+ end
37
+
38
+ def next
39
+ sleep interval
40
+ value
41
+ end
42
+
43
+ def value
44
+ block.call(distance_in_words)
45
+ end
46
+
47
+ def distance_in_words(now = Time.now)
48
+ distance = now - @start
49
+
50
+ if distance < MINUTE
51
+ pluralize(distance, "sec")
52
+ elsif distance < HOUR
53
+ pluralize(distance / MINUTE, "min")
54
+ else
55
+ pluralize(distance / HOUR, "hour")
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def pluralize(amount, unit)
62
+ "#{amount.to_i} #{amount.to_i == 1 ? unit : "#{unit}s"}"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,130 @@
1
+ module Spring
2
+ ORIGINAL_ENV = ENV.to_hash
3
+ end
4
+
5
+ require "spring-jruby/boot"
6
+ require "spring-jruby/impl/application_manager"
7
+
8
+ # Must be last, as it requires bundler/setup, which alters the load path
9
+ require "spring-jruby/commands"
10
+
11
+ module Spring
12
+ class Server
13
+ def self.boot
14
+ new.boot
15
+ end
16
+
17
+ attr_reader :env
18
+
19
+ def initialize(env = Env.new)
20
+ @env = env
21
+ @applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k) }
22
+ @pidfile = env.pidfile_path.open('a')
23
+ @mutex = Mutex.new
24
+ end
25
+
26
+ def log(message)
27
+ env.log "[server] #{message}"
28
+ end
29
+
30
+ def boot
31
+ Spring.verify_environment
32
+
33
+ write_pidfile
34
+ set_pgid
35
+ ignore_signals
36
+ set_exit_hook
37
+ set_process_title
38
+ start_server
39
+ end
40
+
41
+ def start_server
42
+ server = UNIXServer.open(env.socket_name)
43
+ log "started on #{env.socket_name}"
44
+ loop { serve server.accept }
45
+ end
46
+
47
+ def serve(client)
48
+ log "accepted client"
49
+ client.puts env.version
50
+
51
+ app_client = IOWrapper.recv_io(client)
52
+ command = JSON.load(client.read(client.gets.to_i))
53
+
54
+ args, default_rails_env = command.values_at('args', 'default_rails_env')
55
+
56
+ if Spring.command?(args.first)
57
+ log "running command #{args.first}"
58
+ client.puts
59
+ client.puts @applications[rails_env_for(args, default_rails_env)].run(app_client)
60
+ else
61
+ log "command not found #{args.first}"
62
+ client.close
63
+ end
64
+ rescue SocketError => e
65
+ raise e unless client.eof?
66
+ ensure
67
+ redirect_output
68
+ end
69
+
70
+ def rails_env_for(args, default_rails_env)
71
+ Spring.command(args.first).env(args.drop(1)) || default_rails_env
72
+ end
73
+
74
+ # Boot the server into the process group of the current session.
75
+ # This will cause it to be automatically killed once the session
76
+ # ends (i.e. when the user closes their terminal).
77
+ def set_pgid
78
+ Process.setpgid(0, SID.pgid)
79
+ end
80
+
81
+ # Ignore SIGINT and SIGQUIT otherwise the user typing ^C or ^\ on the command line
82
+ # will kill the server/application.
83
+ def ignore_signals
84
+ IGNORE_SIGNALS.each { |sig| trap(sig, "IGNORE") }
85
+ end
86
+
87
+ def set_exit_hook
88
+ server_pid = Process.pid
89
+
90
+ # We don't want this hook to run in any forks of the current process
91
+ at_exit { shutdown if Process.pid == server_pid }
92
+ end
93
+
94
+ def shutdown
95
+ log "shutting down"
96
+
97
+ [env.socket_path, env.pidfile_path].each do |path|
98
+ if path.exist?
99
+ path.unlink rescue nil
100
+ end
101
+ end
102
+
103
+ @applications.values.map { |a| Thread.new { a.stop } }.map(&:join)
104
+ end
105
+
106
+ def write_pidfile
107
+ if @pidfile.flock(File::LOCK_EX | File::LOCK_NB)
108
+ @pidfile.truncate(0)
109
+ @pidfile.write("#{Process.pid}\n")
110
+ @pidfile.fsync
111
+ else
112
+ exit 1
113
+ end
114
+ end
115
+
116
+ # We need to redirect STDOUT and STDERR, otherwise the server will
117
+ # keep the original FDs open which would break piping. (e.g.
118
+ # `spring rake -T | grep db` would hang forever because the server
119
+ # would keep the stdout FD open.)
120
+ def redirect_output
121
+ [STDOUT, STDERR].each { |stream| stream.reopen(env.log_file) }
122
+ end
123
+
124
+ def set_process_title
125
+ ProcessTitleUpdater.run { |distance|
126
+ "spring server | #{env.app_name} | started #{distance} ago"
127
+ }
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,42 @@
1
+ begin
2
+ # If rubygems is present, keep it out of the way when loading fiddle,
3
+ # otherwise if fiddle is not installed then rubygems will load all
4
+ # gemspecs in its (futile) search for fiddle, which is slow.
5
+ if respond_to?(:gem_original_require, true)
6
+ gem_original_require 'fiddle'
7
+ else
8
+ require 'fiddle'
9
+ end
10
+ rescue LoadError
11
+ end
12
+
13
+ module Spring
14
+ module SID
15
+ def self.fiddle_func
16
+ @fiddle_func ||= Fiddle::Function.new(
17
+ DL::Handle::DEFAULT['getsid'],
18
+ [Fiddle::TYPE_INT],
19
+ Fiddle::TYPE_INT
20
+ )
21
+ end
22
+
23
+ def self.sid
24
+ @sid ||= begin
25
+ if Process.respond_to?(:getsid)
26
+ # Ruby 2
27
+ Process.getsid
28
+ elsif defined?(Fiddle) and defined?(DL)
29
+ # Ruby 1.9.3 compiled with libffi support
30
+ fiddle_func.call(0)
31
+ else
32
+ # last resort: shell out
33
+ `ps -p #{Process.pid} -o sess=`.to_i
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.pgid
39
+ Process.getpgid(sid)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ require "active_support"
2
+ require "active_support/test_case"
3
+
4
+ ActiveSupport.test_order = :random
5
+
6
+ module Spring
7
+ module Test
8
+ class << self
9
+ attr_accessor :root
10
+ end
11
+
12
+ require "spring-jruby/test/application"
13
+ require "spring-jruby/test/application_generator"
14
+ require "spring-jruby/test/rails_version"
15
+ require "spring-jruby/test/watcher_test"
16
+ require "spring-jruby/test/acceptance_test"
17
+ end
18
+ end
@@ -0,0 +1,371 @@
1
+ require "io/wait"
2
+ require "timeout"
3
+ require "spring-jruby/sid"
4
+ require "spring-jruby/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'] || '~> 4.2.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 assert_output(artifacts, expected)
32
+ expected.each do |stream, output|
33
+ assert artifacts[stream].include?(output),
34
+ "expected #{stream} to include '#{output}'.\n\n#{app.debug(artifacts)}"
35
+ end
36
+ end
37
+
38
+ def assert_success(command, expected_output = nil)
39
+ artifacts = app.run(*Array(command))
40
+ assert artifacts[:status].success?, "expected successful exit status\n\n#{app.debug(artifacts)}"
41
+ assert_output artifacts, expected_output if expected_output
42
+ end
43
+
44
+ def assert_failure(command, expected_output = nil)
45
+ artifacts = app.run(*Array(command))
46
+ assert !artifacts[:status].success?, "expected unsuccessful exit status\n\n#{app.debug(artifacts)}"
47
+ assert_output artifacts, expected_output if expected_output
48
+ end
49
+
50
+ def assert_speedup(ratio = DEFAULT_SPEEDUP)
51
+ if ENV['CI']
52
+ yield
53
+ else
54
+ app.with_timing do
55
+ yield
56
+ assert app.timing_ratio < ratio, "#{app.last_time} was not less than #{ratio} of #{app.first_time}"
57
+ end
58
+ end
59
+ end
60
+
61
+ def without_gem(name)
62
+ gem_home = app.gem_home.join('gems')
63
+ FileUtils.mv(gem_home.join(name), app.root)
64
+ yield
65
+ ensure
66
+ FileUtils.mv(app.root.join(name), gem_home)
67
+ end
68
+
69
+ setup do
70
+ generator.generate_if_missing
71
+ generator.install_spring
72
+ generator.copy_to(app.root)
73
+ end
74
+
75
+ teardown do
76
+ app.stop_spring
77
+ end
78
+
79
+ test "basic" do
80
+ assert_speedup do
81
+ 2.times { app.run app.spring_test_command }
82
+ end
83
+ end
84
+
85
+ test "help message when called without arguments" do
86
+ assert_success "bin/spring", stdout: 'Usage: spring COMMAND [ARGS]'
87
+ assert app.spring_env.server_running?
88
+ end
89
+
90
+ test "shows help" do
91
+ assert_success "bin/spring help", stdout: 'Usage: spring COMMAND [ARGS]'
92
+ assert_success "bin/spring -h", stdout: 'Usage: spring COMMAND [ARGS]'
93
+ assert_success "bin/spring --help", stdout: 'Usage: spring COMMAND [ARGS]'
94
+ refute app.spring_env.server_running?
95
+ end
96
+
97
+ test "test changes are picked up" do
98
+ assert_speedup do
99
+ assert_success app.spring_test_command, stdout: "0 failures"
100
+
101
+ File.write(app.test, app.test.read.sub("get :index", "raise 'omg'"))
102
+ assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
103
+ end
104
+ end
105
+
106
+ test "code changes are picked up" do
107
+ assert_speedup do
108
+ assert_success app.spring_test_command, stdout: "0 failures"
109
+
110
+ File.write(app.controller, app.controller.read.sub("@posts = Post.all", "raise 'omg'"))
111
+ assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
112
+ end
113
+ end
114
+
115
+ test "code changes in pre-referenced app files are picked up" do
116
+ File.write(app.path("config/initializers/load_posts_controller.rb"), "PostsController\n")
117
+
118
+ assert_speedup do
119
+ assert_success app.spring_test_command, stdout: "0 failures"
120
+
121
+ File.write(app.controller, app.controller.read.sub("@posts = Post.all", "raise 'omg'"))
122
+ assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
123
+ end
124
+ end
125
+
126
+ test "app gets reloaded when preloaded files change" do
127
+ assert_success app.spring_test_command
128
+
129
+ File.write(app.application_config, app.application_config.read + <<-RUBY.strip_heredoc)
130
+ class Foo
131
+ def self.omg
132
+ raise "omg"
133
+ end
134
+ end
135
+ RUBY
136
+ File.write(app.test, app.test.read.sub("get :index", "Foo.omg"))
137
+
138
+ app.await_reload
139
+ assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
140
+ end
141
+
142
+ test "app gets reloaded even with a ton of boot output" do
143
+ limit = UNIXSocket.pair.first.getsockopt(:SOCKET, :SNDBUF).int
144
+
145
+ assert_success app.spring_test_command
146
+ File.write(app.path("config/initializers/verbose.rb"), "#{limit}.times { puts 'x' }")
147
+
148
+ app.await_reload
149
+ assert_success app.spring_test_command
150
+ end
151
+
152
+ test "app recovers when a boot-level error is introduced" do
153
+ config = app.application_config.read
154
+
155
+ assert_success app.spring_test_command
156
+
157
+ File.write(app.application_config, "#{config}\nomg")
158
+ app.await_reload
159
+
160
+ assert_failure app.spring_test_command
161
+
162
+ File.write(app.application_config, config)
163
+ assert_success app.spring_test_command
164
+ end
165
+
166
+ test "stop command kills server" do
167
+ app.run app.spring_test_command
168
+ assert app.spring_env.server_running?, "The server should be running but it isn't"
169
+
170
+ assert_success "bin/spring stop"
171
+ assert !app.spring_env.server_running?, "The server should not be running but it is"
172
+ end
173
+
174
+ test "custom commands" do
175
+ # Start spring before setting up the command, to test that it gracefully upgrades itself
176
+ assert_success "bin/rails runner ''"
177
+
178
+ File.write(app.spring_config, <<-RUBY.strip_heredoc)
179
+ class CustomCommand
180
+ def call
181
+ puts "omg"
182
+ end
183
+
184
+ def exec_name
185
+ "rake"
186
+ end
187
+ end
188
+
189
+ Spring.register_command "custom", CustomCommand.new
190
+ RUBY
191
+
192
+ assert_success "bin/spring custom", stdout: "omg"
193
+
194
+ assert_success "bin/spring binstub custom"
195
+ assert_success "bin/custom", stdout: "omg"
196
+
197
+ app.env["DISABLE_SPRING"] = "1"
198
+ assert_success %{bin/custom -e 'puts "foo"'}, stdout: "foo"
199
+ end
200
+
201
+ test "binstub" do
202
+ assert_success "bin/rails server --help", stdout: "Usage: rails server" # rails command fallback
203
+
204
+ assert_success "#{app.spring} binstub rake", stdout: "bin/rake: spring already present"
205
+
206
+ assert_success "#{app.spring} binstub --remove rake", stdout: "bin/rake: spring removed"
207
+ assert !app.path("bin/rake").read.include?(Spring::Client::Binstub::LOADER)
208
+ assert_success "bin/rake -T", stdout: "rake db:migrate"
209
+ end
210
+
211
+ test "binstub when spring is uninstalled" do
212
+ without_gem "spring-#{Spring::VERSION}" do
213
+ File.write(app.gemfile, app.gemfile.read.gsub(/gem 'spring.*/, ""))
214
+ assert_success "bin/rake -T", stdout: "rake db:migrate"
215
+ end
216
+ end
217
+
218
+ test "binstub upgrade" do
219
+ File.write(app.path("bin/rake"), <<-RUBY.strip_heredoc)
220
+ #!/usr/bin/env ruby
221
+
222
+ if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?
223
+ exec "bundle", "exec", "rake", *ARGV
224
+ else
225
+ ARGV.unshift "rake"
226
+ load Gem.bin_path("spring", "spring")
227
+ end
228
+ RUBY
229
+
230
+ File.write(app.path("bin/rails"), <<-RUBY.strip_heredoc)
231
+ #!/usr/bin/env ruby
232
+
233
+ if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?
234
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
235
+ require_relative '../config/boot'
236
+ require 'rails/commands'
237
+ else
238
+ ARGV.unshift "rails"
239
+ load Gem.bin_path("spring", "spring")
240
+ end
241
+ RUBY
242
+
243
+ assert_success "bin/spring binstub --all", stdout: "upgraded"
244
+
245
+ expected = <<-RUBY.gsub(/^ /, "")
246
+ #!/usr/bin/env ruby
247
+ #{Spring::Client::Binstub::LOADER.strip}
248
+ require 'bundler/setup'
249
+ load Gem.bin_path('rake', 'rake')
250
+ RUBY
251
+ assert_equal expected, app.path("bin/rake").read
252
+
253
+ expected = <<-RUBY.gsub(/^ /, "")
254
+ #!/usr/bin/env ruby
255
+ #{Spring::Client::Binstub::LOADER.strip}
256
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
257
+ require_relative '../config/boot'
258
+ require 'rails/commands'
259
+ RUBY
260
+ assert_equal expected, app.path("bin/rails").read
261
+ end
262
+
263
+ test "after fork callback" do
264
+ File.write(app.spring_config, "Spring.after_fork { puts '!callback!' }")
265
+ assert_success "bin/rails runner 'puts 2'", stdout: "!callback!\n2"
266
+ end
267
+
268
+ test "global config file evaluated" do
269
+ File.write("#{app.user_home}/.spring.rb", "Spring.after_fork { puts '!callback!' }")
270
+ assert_success "bin/rails runner 'puts 2'", stdout: "!callback!\n2"
271
+ end
272
+
273
+ test "can define client tasks" do
274
+ File.write("#{app.spring_config.sub('.rb', '_client.rb')}", <<-RUBY)
275
+ Spring::Client::COMMANDS["foo"] = lambda { |args| puts "bar -- \#{args.inspect}" }
276
+ RUBY
277
+ assert_success "bin/spring foo --baz", stdout: "bar -- [\"foo\", \"--baz\"]\n"
278
+ end
279
+
280
+ test "missing config/application.rb" do
281
+ app.application_config.delete
282
+ assert_failure "bin/rake -T", stderr: "unable to find your config/application.rb"
283
+ end
284
+
285
+ test "piping" do
286
+ assert_success "bin/rake -T | grep db", stdout: "rake db:migrate"
287
+ end
288
+
289
+ test "status" do
290
+ assert_success "bin/spring status", stdout: "Spring is not running"
291
+ assert_success "bin/rails runner ''"
292
+ assert_success "bin/spring status", stdout: "Spring is running"
293
+ end
294
+
295
+ test "runner command sets Rails environment from command-line options" do
296
+ assert_success "bin/rails runner -e test 'puts Rails.env'", stdout: "test"
297
+ assert_success "bin/rails runner --environment=test 'puts Rails.env'", stdout: "test"
298
+ end
299
+
300
+ test "forcing rails env via environment variable" do
301
+ app.env['RAILS_ENV'] = 'test'
302
+ assert_success "bin/rake -p 'Rails.env'", stdout: "test"
303
+ end
304
+
305
+ test "setting env vars with rake" do
306
+ File.write(app.path("lib/tasks/env.rake"), <<-RUBY.strip_heredoc)
307
+ task :print_rails_env => :environment do
308
+ puts Rails.env
309
+ end
310
+
311
+ task :print_env do
312
+ ENV.each { |k, v| puts "\#{k}=\#{v}" }
313
+ end
314
+
315
+ task(:default).clear.enhance [:print_rails_env]
316
+ RUBY
317
+
318
+ assert_success "bin/rake RAILS_ENV=test print_rails_env", stdout: "test"
319
+ assert_success "bin/rake FOO=bar print_env", stdout: "FOO=bar"
320
+ assert_success "bin/rake", stdout: "test"
321
+ end
322
+
323
+ test "changing the Gemfile works" do
324
+ assert_success %(bin/rails runner 'require "sqlite3"')
325
+
326
+ File.write(app.gemfile, app.gemfile.read.sub(%{gem 'sqlite3'}, %{# gem 'sqlite3'}))
327
+ app.await_reload
328
+
329
+ assert_failure %(bin/rails runner 'require "sqlite3"'), stderr: "sqlite3"
330
+ end
331
+
332
+ test "changing the Gemfile works when spring calls into itself" do
333
+ File.write(app.path("script.rb"), <<-RUBY.strip_heredoc)
334
+ gemfile = Rails.root.join("Gemfile")
335
+ File.write(gemfile, "\#{gemfile.read}gem 'devise'\\n")
336
+ Bundler.with_clean_env do
337
+ system(#{app.env.inspect}, "bundle install")
338
+ end
339
+ output = `\#{Rails.root.join('bin/rails')} runner 'require "devise"; puts "done";'`
340
+ exit output == "done\n"
341
+ RUBY
342
+
343
+ assert_success [%(bin/rails runner 'load Rails.root.join("script.rb")'), timeout: 60]
344
+ end
345
+
346
+ test "changing the environment between runs" do
347
+ File.write(app.application_config, "#{app.application_config.read}\nENV['BAR'] = 'bar'")
348
+
349
+ app.env["OMG"] = "1"
350
+ app.env["FOO"] = "1"
351
+ app.env["RUBYOPT"] = "-rubygems"
352
+
353
+ assert_success %(bin/rails runner 'p ENV["OMG"]'), stdout: "1"
354
+ assert_success %(bin/rails runner 'p ENV["BAR"]'), stdout: "bar"
355
+ assert_success %(bin/rails runner 'p ENV.key?("BUNDLE_GEMFILE")'), stdout: "true"
356
+ assert_success %(bin/rails runner 'p ENV["RUBYOPT"]'), stdout: "bundler"
357
+
358
+ app.env["OMG"] = "2"
359
+ app.env.delete "FOO"
360
+
361
+ assert_success %(bin/rails runner 'p ENV["OMG"]'), stdout: "2"
362
+ assert_success %(bin/rails runner 'p ENV.key?("FOO")'), stdout: "false"
363
+ end
364
+
365
+ test "Kernel.raise remains private" do
366
+ expr = "p Kernel.private_instance_methods.include?(:raise)"
367
+ assert_success %(bin/rails runner '#{expr}'), stdout: "true"
368
+ end
369
+ end
370
+ end
371
+ end