spring 1.0.0 → 1.3.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +6 -1
- data/CHANGELOG.md +81 -0
- data/CONTRIBUTING.md +52 -0
- data/Gemfile +0 -2
- data/README.md +137 -54
- data/Rakefile +1 -1
- data/bin/spring +17 -0
- data/lib/spring/application/boot.rb +18 -0
- data/lib/spring/application.rb +183 -76
- data/lib/spring/application_manager.rb +54 -40
- data/lib/spring/binstub.rb +13 -0
- data/lib/spring/boot.rb +8 -0
- data/lib/spring/client/binstub.rb +150 -39
- data/lib/spring/client/help.rb +4 -12
- data/lib/spring/client/rails.rb +2 -3
- data/lib/spring/client/run.rb +69 -11
- data/lib/spring/client/status.rb +2 -2
- data/lib/spring/client/stop.rb +6 -21
- data/lib/spring/client/version.rb +11 -0
- data/lib/spring/client.rb +8 -5
- data/lib/spring/command_wrapper.rb +82 -0
- data/lib/spring/commands/rails.rb +44 -13
- data/lib/spring/commands/rake.rb +0 -4
- data/lib/spring/commands.rb +14 -4
- data/lib/spring/configuration.rb +11 -2
- data/lib/spring/env.rb +36 -10
- data/lib/spring/server.rb +9 -35
- data/lib/spring/sid.rb +1 -1
- data/lib/spring/test/acceptance_test.rb +346 -0
- data/lib/spring/test/application.rb +217 -0
- data/lib/spring/test/application_generator.rb +121 -0
- data/lib/spring/test/rails_version.rb +40 -0
- data/lib/spring/test/watcher_test.rb +167 -0
- data/lib/spring/test.rb +18 -0
- data/lib/spring/version.rb +1 -1
- data/lib/spring/watcher/abstract.rb +7 -8
- data/lib/spring/watcher/polling.rb +2 -0
- data/lib/spring/watcher.rb +4 -8
- data/spring.gemspec +2 -2
- data/test/acceptance_test.rb +4 -0
- data/test/helper.rb +2 -2
- data/test/unit/client/version_test.rb +14 -0
- data/test/unit/commands_test.rb +23 -1
- data/test/unit/watcher_test.rb +2 -184
- metadata +30 -17
- data/lib/spring/watcher/listen.rb +0 -52
- data/test/acceptance/app_test.rb +0 -511
data/lib/spring/commands.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "spring/watcher"
|
|
2
|
+
require "spring/command_wrapper"
|
|
2
3
|
|
|
3
4
|
module Spring
|
|
4
5
|
@commands = {}
|
|
@@ -7,8 +8,8 @@ module Spring
|
|
|
7
8
|
attr_reader :commands
|
|
8
9
|
end
|
|
9
10
|
|
|
10
|
-
def self.register_command(name,
|
|
11
|
-
commands[name] =
|
|
11
|
+
def self.register_command(name, command = nil)
|
|
12
|
+
commands[name] = CommandWrapper.new(name, command)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def self.command?(name)
|
|
@@ -31,8 +32,17 @@ module Spring
|
|
|
31
32
|
# then we need to be under bundler.
|
|
32
33
|
require "bundler/setup"
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
# Auto-require any spring extensions which are in the Gemfile
|
|
36
|
+
Gem::Specification.map(&:name).grep(/^spring-/).each do |command|
|
|
37
|
+
begin
|
|
38
|
+
require command
|
|
39
|
+
rescue LoadError => error
|
|
40
|
+
if error.message.include?(command)
|
|
41
|
+
require command.gsub("-", "/")
|
|
42
|
+
else
|
|
43
|
+
raise
|
|
44
|
+
end
|
|
45
|
+
end
|
|
36
46
|
end
|
|
37
47
|
|
|
38
48
|
config = File.expand_path("./config/spring.rb")
|
data/lib/spring/configuration.rb
CHANGED
|
@@ -22,15 +22,24 @@ module Spring
|
|
|
22
22
|
|
|
23
23
|
def application_root_path
|
|
24
24
|
@application_root_path ||= begin
|
|
25
|
-
|
|
25
|
+
if application_root
|
|
26
|
+
path = Pathname.new(File.expand_path(application_root))
|
|
27
|
+
else
|
|
28
|
+
path = project_root_path
|
|
29
|
+
end
|
|
30
|
+
|
|
26
31
|
raise MissingApplication.new(path) unless path.join("config/application.rb").exist?
|
|
27
32
|
path
|
|
28
33
|
end
|
|
29
34
|
end
|
|
30
35
|
|
|
36
|
+
def project_root_path
|
|
37
|
+
@project_root_path ||= find_project_root(Pathname.new(File.expand_path(Dir.pwd)))
|
|
38
|
+
end
|
|
39
|
+
|
|
31
40
|
private
|
|
32
41
|
|
|
33
|
-
def find_project_root(current_dir
|
|
42
|
+
def find_project_root(current_dir)
|
|
34
43
|
if current_dir.join(gemfile).exist?
|
|
35
44
|
current_dir
|
|
36
45
|
elsif current_dir.root?
|
data/lib/spring/env.rb
CHANGED
|
@@ -9,31 +9,37 @@ require "spring/configuration"
|
|
|
9
9
|
|
|
10
10
|
module Spring
|
|
11
11
|
IGNORE_SIGNALS = %w(INT QUIT)
|
|
12
|
+
STOP_TIMEOUT = 2 # seconds
|
|
12
13
|
|
|
13
14
|
class Env
|
|
14
15
|
attr_reader :log_file
|
|
15
16
|
|
|
16
17
|
def initialize(root = nil)
|
|
17
|
-
@root
|
|
18
|
-
@
|
|
18
|
+
@root = root
|
|
19
|
+
@project_root = root
|
|
20
|
+
@log_file = File.open(ENV["SPRING_LOG"] || File::NULL, "a")
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
def root
|
|
22
24
|
@root ||= Spring.application_root_path
|
|
23
25
|
end
|
|
24
26
|
|
|
27
|
+
def project_root
|
|
28
|
+
@project_root ||= Spring.project_root_path
|
|
29
|
+
end
|
|
30
|
+
|
|
25
31
|
def version
|
|
26
32
|
Spring::VERSION
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def tmp_path
|
|
30
|
-
path = Pathname.new(Dir.tmpdir
|
|
36
|
+
path = Pathname.new(File.join(ENV['XDG_RUNTIME_DIR'] || Dir.tmpdir, "spring"))
|
|
31
37
|
FileUtils.mkdir_p(path) unless path.exist?
|
|
32
38
|
path
|
|
33
39
|
end
|
|
34
40
|
|
|
35
41
|
def application_id
|
|
36
|
-
Digest::MD5.hexdigest(
|
|
42
|
+
Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
def socket_path
|
|
@@ -60,7 +66,7 @@ module Spring
|
|
|
60
66
|
end
|
|
61
67
|
|
|
62
68
|
def server_running?
|
|
63
|
-
pidfile = pidfile_path.open('r')
|
|
69
|
+
pidfile = pidfile_path.open('r+')
|
|
64
70
|
!pidfile.flock(File::LOCK_EX | File::LOCK_NB)
|
|
65
71
|
rescue Errno::ENOENT
|
|
66
72
|
false
|
|
@@ -71,13 +77,33 @@ module Spring
|
|
|
71
77
|
end
|
|
72
78
|
end
|
|
73
79
|
|
|
74
|
-
def bundle_mtime
|
|
75
|
-
[Bundler.default_lockfile, Bundler.default_gemfile].select(&:exist?).map(&:mtime).max
|
|
76
|
-
end
|
|
77
|
-
|
|
78
80
|
def log(message)
|
|
79
|
-
log_file.puts "[#{Time.now}] #{message}"
|
|
81
|
+
log_file.puts "[#{Time.now}] [#{Process.pid}] #{message}"
|
|
80
82
|
log_file.flush
|
|
81
83
|
end
|
|
84
|
+
|
|
85
|
+
def stop
|
|
86
|
+
if server_running?
|
|
87
|
+
timeout = Time.now + STOP_TIMEOUT
|
|
88
|
+
kill 'TERM'
|
|
89
|
+
sleep 0.1 until !server_running? || Time.now >= timeout
|
|
90
|
+
|
|
91
|
+
if server_running?
|
|
92
|
+
kill 'KILL'
|
|
93
|
+
:killed
|
|
94
|
+
else
|
|
95
|
+
:stopped
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
:not_running
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def kill(sig)
|
|
103
|
+
pid = self.pid
|
|
104
|
+
Process.kill(sig, pid) if pid
|
|
105
|
+
rescue Errno::ESRCH
|
|
106
|
+
# already dead
|
|
107
|
+
end
|
|
82
108
|
end
|
|
83
109
|
end
|
data/lib/spring/server.rb
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
module Spring
|
|
2
|
-
|
|
3
|
-
attr_reader :original_env
|
|
4
|
-
end
|
|
5
|
-
@original_env = ENV.to_hash
|
|
2
|
+
ORIGINAL_ENV = ENV.to_hash
|
|
6
3
|
end
|
|
7
4
|
|
|
8
|
-
require "
|
|
9
|
-
require "thread"
|
|
10
|
-
|
|
11
|
-
require "spring/configuration"
|
|
12
|
-
require "spring/env"
|
|
5
|
+
require "spring/boot"
|
|
13
6
|
require "spring/application_manager"
|
|
14
|
-
require "spring/process_title_updater"
|
|
15
|
-
require "spring/json"
|
|
16
7
|
|
|
17
|
-
# Must be last, as it requires bundler/setup
|
|
8
|
+
# Must be last, as it requires bundler/setup, which alters the load path
|
|
18
9
|
require "spring/commands"
|
|
19
10
|
|
|
20
|
-
# readline must be required before we setpgid, otherwise the require may hang,
|
|
21
|
-
# if readline has been built against libedit. See issue #70.
|
|
22
|
-
require "readline"
|
|
23
|
-
|
|
24
11
|
module Spring
|
|
25
12
|
class Server
|
|
26
13
|
def self.boot
|
|
@@ -31,7 +18,7 @@ module Spring
|
|
|
31
18
|
|
|
32
19
|
def initialize(env = Env.new)
|
|
33
20
|
@env = env
|
|
34
|
-
@applications = Hash.new { |h, k| h[k] = ApplicationManager.new(
|
|
21
|
+
@applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k) }
|
|
35
22
|
@pidfile = env.pidfile_path.open('a')
|
|
36
23
|
@mutex = Mutex.new
|
|
37
24
|
end
|
|
@@ -48,7 +35,6 @@ module Spring
|
|
|
48
35
|
ignore_signals
|
|
49
36
|
set_exit_hook
|
|
50
37
|
set_process_title
|
|
51
|
-
watch_bundle
|
|
52
38
|
start_server
|
|
53
39
|
end
|
|
54
40
|
|
|
@@ -82,13 +68,7 @@ module Spring
|
|
|
82
68
|
end
|
|
83
69
|
|
|
84
70
|
def rails_env_for(args, default_rails_env)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if command.respond_to?(:env)
|
|
88
|
-
env = command.env(args.drop(1))
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
env || default_rails_env
|
|
71
|
+
Spring.command(args.first).env(args.drop(1)) || default_rails_env
|
|
92
72
|
end
|
|
93
73
|
|
|
94
74
|
# Boot the server into the process group of the current session.
|
|
@@ -101,7 +81,7 @@ module Spring
|
|
|
101
81
|
# Ignore SIGINT and SIGQUIT otherwise the user typing ^C or ^\ on the command line
|
|
102
82
|
# will kill the server/application.
|
|
103
83
|
def ignore_signals
|
|
104
|
-
IGNORE_SIGNALS.each { |sig| trap(sig,
|
|
84
|
+
IGNORE_SIGNALS.each { |sig| trap(sig, "IGNORE") }
|
|
105
85
|
end
|
|
106
86
|
|
|
107
87
|
def set_exit_hook
|
|
@@ -112,13 +92,15 @@ module Spring
|
|
|
112
92
|
end
|
|
113
93
|
|
|
114
94
|
def shutdown
|
|
115
|
-
|
|
95
|
+
log "shutting down"
|
|
116
96
|
|
|
117
97
|
[env.socket_path, env.pidfile_path].each do |path|
|
|
118
98
|
if path.exist?
|
|
119
99
|
path.unlink rescue nil
|
|
120
100
|
end
|
|
121
101
|
end
|
|
102
|
+
|
|
103
|
+
@applications.values.map { |a| Thread.new { a.stop } }.map(&:join)
|
|
122
104
|
end
|
|
123
105
|
|
|
124
106
|
def write_pidfile
|
|
@@ -144,13 +126,5 @@ module Spring
|
|
|
144
126
|
"spring server | #{env.app_name} | started #{distance} ago"
|
|
145
127
|
}
|
|
146
128
|
end
|
|
147
|
-
|
|
148
|
-
def watch_bundle
|
|
149
|
-
@bundle_mtime = env.bundle_mtime
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def application_starting
|
|
153
|
-
@mutex.synchronize { exit if env.bundle_mtime != @bundle_mtime }
|
|
154
|
-
end
|
|
155
129
|
end
|
|
156
130
|
end
|
data/lib/spring/sid.rb
CHANGED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require "io/wait"
|
|
4
|
+
require "timeout"
|
|
5
|
+
require "spring/sid"
|
|
6
|
+
require "spring/client"
|
|
7
|
+
|
|
8
|
+
module Spring
|
|
9
|
+
module Test
|
|
10
|
+
class AcceptanceTest < ActiveSupport::TestCase
|
|
11
|
+
runnables.delete self # prevent Minitest running this class
|
|
12
|
+
|
|
13
|
+
DEFAULT_SPEEDUP = 0.8
|
|
14
|
+
|
|
15
|
+
def rails_version
|
|
16
|
+
ENV['RAILS_VERSION'] || '~> 4.2.0'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Extension point for spring-watchers-listen
|
|
20
|
+
def generator_klass
|
|
21
|
+
Spring::Test::ApplicationGenerator
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def generator
|
|
25
|
+
@@generator ||= generator_klass.new(rails_version)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def app
|
|
29
|
+
@app ||= Spring::Test::Application.new("#{Spring::Test.root}/apps/tmp")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def assert_output(artifacts, expected)
|
|
33
|
+
expected.each do |stream, output|
|
|
34
|
+
assert artifacts[stream].include?(output),
|
|
35
|
+
"expected #{stream} to include '#{output}'.\n\n#{app.debug(artifacts)}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def assert_success(command, expected_output = nil)
|
|
40
|
+
artifacts = app.run(*Array(command))
|
|
41
|
+
assert artifacts[:status].success?, "expected successful exit status\n\n#{app.debug(artifacts)}"
|
|
42
|
+
assert_output artifacts, expected_output if expected_output
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def assert_failure(command, expected_output = nil)
|
|
46
|
+
artifacts = app.run(*Array(command))
|
|
47
|
+
assert !artifacts[:status].success?, "expected unsuccessful exit status\n\n#{app.debug(artifacts)}"
|
|
48
|
+
assert_output artifacts, expected_output if expected_output
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def assert_speedup(ratio = DEFAULT_SPEEDUP)
|
|
52
|
+
if ENV['CI']
|
|
53
|
+
yield
|
|
54
|
+
else
|
|
55
|
+
app.with_timing do
|
|
56
|
+
yield
|
|
57
|
+
assert app.timing_ratio < ratio, "#{app.last_time} was not less than #{ratio} of #{app.first_time}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
setup do
|
|
63
|
+
generator.generate_if_missing
|
|
64
|
+
generator.install_spring
|
|
65
|
+
generator.copy_to(app.root)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
teardown do
|
|
69
|
+
app.stop_spring
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
test "basic" do
|
|
73
|
+
assert_speedup do
|
|
74
|
+
2.times { app.run app.spring_test_command }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
test "help message when called without arguments" do
|
|
79
|
+
assert_success "bin/spring", stdout: 'Usage: spring COMMAND [ARGS]'
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
test "test changes are picked up" do
|
|
83
|
+
assert_speedup do
|
|
84
|
+
assert_success app.spring_test_command, stdout: "0 failures"
|
|
85
|
+
|
|
86
|
+
File.write(app.test, app.test.read.sub("get :index", "raise 'omg'"))
|
|
87
|
+
assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
test "code changes are picked up" do
|
|
92
|
+
assert_speedup do
|
|
93
|
+
assert_success app.spring_test_command, stdout: "0 failures"
|
|
94
|
+
|
|
95
|
+
File.write(app.controller, app.controller.read.sub("@posts = Post.all", "raise 'omg'"))
|
|
96
|
+
assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
test "code changes in pre-referenced app files are picked up" do
|
|
101
|
+
File.write(app.path("config/initializers/load_posts_controller.rb"), "PostsController\n")
|
|
102
|
+
|
|
103
|
+
assert_speedup do
|
|
104
|
+
assert_success app.spring_test_command, stdout: "0 failures"
|
|
105
|
+
|
|
106
|
+
File.write(app.controller, app.controller.read.sub("@posts = Post.all", "raise 'omg'"))
|
|
107
|
+
assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
test "app gets reloaded when preloaded files change" do
|
|
112
|
+
assert_success app.spring_test_command
|
|
113
|
+
|
|
114
|
+
File.write(app.application_config, app.application_config.read + <<-CODE)
|
|
115
|
+
class Foo
|
|
116
|
+
def self.omg
|
|
117
|
+
raise "omg"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
CODE
|
|
121
|
+
File.write(app.test, app.test.read.sub("get :index", "Foo.omg"))
|
|
122
|
+
|
|
123
|
+
app.await_reload
|
|
124
|
+
assert_failure app.spring_test_command, stdout: "RuntimeError: omg"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
test "app gets reloaded even with a ton of boot output" do
|
|
128
|
+
limit = UNIXSocket.pair.first.getsockopt(:SOCKET, :SNDBUF).int
|
|
129
|
+
|
|
130
|
+
assert_success app.spring_test_command
|
|
131
|
+
File.write(app.path("config/initializers/verbose.rb"), "#{limit}.times { puts 'x' }")
|
|
132
|
+
|
|
133
|
+
app.await_reload
|
|
134
|
+
assert_success app.spring_test_command
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
test "app recovers when a boot-level error is introduced" do
|
|
138
|
+
config = app.application_config.read
|
|
139
|
+
|
|
140
|
+
assert_success app.spring_test_command
|
|
141
|
+
|
|
142
|
+
File.write(app.application_config, "#{config}\nomg")
|
|
143
|
+
app.await_reload
|
|
144
|
+
|
|
145
|
+
assert_failure app.spring_test_command
|
|
146
|
+
|
|
147
|
+
File.write(app.application_config, config)
|
|
148
|
+
assert_success app.spring_test_command
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
test "stop command kills server" do
|
|
152
|
+
app.run app.spring_test_command
|
|
153
|
+
assert app.spring_env.server_running?, "The server should be running but it isn't"
|
|
154
|
+
|
|
155
|
+
assert_success "bin/spring stop"
|
|
156
|
+
assert !app.spring_env.server_running?, "The server should not be running but it is"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
test "custom commands" do
|
|
160
|
+
# Start spring before setting up the command, to test that it gracefully upgrades itself
|
|
161
|
+
assert_success "bin/rails runner ''"
|
|
162
|
+
|
|
163
|
+
File.write(app.spring_config, <<-CODE)
|
|
164
|
+
class CustomCommand
|
|
165
|
+
def call
|
|
166
|
+
puts "omg"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def exec_name
|
|
170
|
+
"rake"
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
Spring.register_command "custom", CustomCommand.new
|
|
175
|
+
CODE
|
|
176
|
+
|
|
177
|
+
assert_success "bin/spring custom", stdout: "omg"
|
|
178
|
+
|
|
179
|
+
assert_success "bin/spring binstub custom"
|
|
180
|
+
assert_success "bin/custom", stdout: "omg"
|
|
181
|
+
|
|
182
|
+
app.env["DISABLE_SPRING"] = "1"
|
|
183
|
+
assert_success %{bin/custom -e 'puts "foo"'}, stdout: "foo"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
test "binstub" do
|
|
187
|
+
assert_success "bin/rails server --help", stdout: "Usage: rails server" # rails command fallback
|
|
188
|
+
|
|
189
|
+
assert_success "#{app.spring} binstub rake", stdout: "bin/rake: spring already present"
|
|
190
|
+
|
|
191
|
+
assert_success "#{app.spring} binstub --remove rake", stdout: "bin/rake: spring removed"
|
|
192
|
+
assert !app.path("bin/rake").read.include?(Spring::Client::Binstub::LOADER)
|
|
193
|
+
assert_success "bin/rake -T", stdout: "rake db:migrate"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
test "binstub when spring is uninstalled" do
|
|
197
|
+
app.run! "gem uninstall --ignore-dependencies spring"
|
|
198
|
+
File.write(app.gemfile, app.gemfile.read.gsub(/gem 'spring.*/, ""))
|
|
199
|
+
assert_success "bin/rake -T", stdout: "rake db:migrate"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
test "binstub upgrade" do
|
|
203
|
+
File.write(app.path("bin/rake"), <<CODE)
|
|
204
|
+
#!/usr/bin/env ruby
|
|
205
|
+
|
|
206
|
+
if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?
|
|
207
|
+
exec "bundle", "exec", "rake", *ARGV
|
|
208
|
+
else
|
|
209
|
+
ARGV.unshift "rake"
|
|
210
|
+
load Gem.bin_path("spring", "spring")
|
|
211
|
+
end
|
|
212
|
+
CODE
|
|
213
|
+
|
|
214
|
+
File.write(app.path("bin/rails"), <<CODE)
|
|
215
|
+
#!/usr/bin/env ruby
|
|
216
|
+
|
|
217
|
+
if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?
|
|
218
|
+
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
|
219
|
+
require_relative '../config/boot'
|
|
220
|
+
require 'rails/commands'
|
|
221
|
+
else
|
|
222
|
+
ARGV.unshift "rails"
|
|
223
|
+
load Gem.bin_path("spring", "spring")
|
|
224
|
+
end
|
|
225
|
+
CODE
|
|
226
|
+
|
|
227
|
+
assert_success "bin/spring binstub --all", stdout: "upgraded"
|
|
228
|
+
|
|
229
|
+
assert_equal app.path("bin/rake").read, <<CODE
|
|
230
|
+
#!/usr/bin/env ruby
|
|
231
|
+
#{Spring::Client::Binstub::LOADER.strip}
|
|
232
|
+
require 'bundler/setup'
|
|
233
|
+
load Gem.bin_path('rake', 'rake')
|
|
234
|
+
CODE
|
|
235
|
+
|
|
236
|
+
assert_equal app.path("bin/rails").read, <<CODE
|
|
237
|
+
#!/usr/bin/env ruby
|
|
238
|
+
#{Spring::Client::Binstub::LOADER.strip}
|
|
239
|
+
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
|
240
|
+
require_relative '../config/boot'
|
|
241
|
+
require 'rails/commands'
|
|
242
|
+
CODE
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
test "after fork callback" do
|
|
246
|
+
File.write(app.spring_config, "Spring.after_fork { puts '!callback!' }")
|
|
247
|
+
assert_success "bin/rails runner 'puts 2'", stdout: "!callback!\n2"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
test "global config file evaluated" do
|
|
251
|
+
File.write("#{app.user_home}/.spring.rb", "Spring.after_fork { puts '!callback!' }")
|
|
252
|
+
assert_success "bin/rails runner 'puts 2'", stdout: "!callback!\n2"
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
test "missing config/application.rb" do
|
|
256
|
+
app.application_config.delete
|
|
257
|
+
assert_failure "bin/rake -T", stderr: "unable to find your config/application.rb"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
test "piping" do
|
|
261
|
+
assert_success "bin/rake -T | grep db", stdout: "rake db:migrate"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
test "status" do
|
|
265
|
+
assert_success "bin/spring status", stdout: "Spring is not running"
|
|
266
|
+
assert_success "bin/rails runner ''"
|
|
267
|
+
assert_success "bin/spring status", stdout: "Spring is running"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
test "runner command sets Rails environment from command-line options" do
|
|
271
|
+
assert_success "bin/rails runner -e test 'puts Rails.env'", stdout: "test"
|
|
272
|
+
assert_success "bin/rails runner --environment=test 'puts Rails.env'", stdout: "test"
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
test "forcing rails env via environment variable" do
|
|
276
|
+
app.env['RAILS_ENV'] = 'test'
|
|
277
|
+
assert_success "bin/rake -p 'Rails.env'", stdout: "test"
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
test "setting env vars with rake" do
|
|
281
|
+
File.write(app.path("lib/tasks/env.rake"), <<-'CODE')
|
|
282
|
+
task :print_rails_env => :environment do
|
|
283
|
+
puts Rails.env
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
task :print_env do
|
|
287
|
+
ENV.each { |k, v| puts "#{k}=#{v}" }
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
task(:default).clear.enhance [:print_rails_env]
|
|
291
|
+
CODE
|
|
292
|
+
|
|
293
|
+
assert_success "bin/rake RAILS_ENV=test print_rails_env", stdout: "test"
|
|
294
|
+
assert_success "bin/rake FOO=bar print_env", stdout: "FOO=bar"
|
|
295
|
+
assert_success "bin/rake", stdout: "test"
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
test "changing the Gemfile works" do
|
|
299
|
+
assert_success %(bin/rails runner 'require "sqlite3"')
|
|
300
|
+
|
|
301
|
+
File.write(app.gemfile, app.gemfile.read.sub(%{gem 'sqlite3'}, %{# gem 'sqlite3'}))
|
|
302
|
+
app.await_reload
|
|
303
|
+
|
|
304
|
+
assert_failure %(bin/rails runner 'require "sqlite3"'), stderr: "sqlite3"
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
test "changing the Gemfile works when spring calls into itself" do
|
|
308
|
+
File.write(app.path("script.rb"), <<-CODE)
|
|
309
|
+
gemfile = Rails.root.join("Gemfile")
|
|
310
|
+
File.write(gemfile, "\#{gemfile.read}gem 'devise'\\n")
|
|
311
|
+
Bundler.with_clean_env do
|
|
312
|
+
system(#{app.env.inspect}, "bundle install")
|
|
313
|
+
end
|
|
314
|
+
output = `\#{Rails.root.join('bin/rails')} runner 'require "devise"; puts "done";'`
|
|
315
|
+
exit output == "done\n"
|
|
316
|
+
CODE
|
|
317
|
+
|
|
318
|
+
assert_success [%(bin/rails runner 'load Rails.root.join("script.rb")'), timeout: 60]
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
test "changing the environment between runs" do
|
|
322
|
+
File.write(app.application_config, "#{app.application_config.read}\nENV['BAR'] = 'bar'")
|
|
323
|
+
|
|
324
|
+
app.env["OMG"] = "1"
|
|
325
|
+
app.env["FOO"] = "1"
|
|
326
|
+
app.env["RUBYOPT"] = "-rubygems"
|
|
327
|
+
|
|
328
|
+
assert_success %(bin/rails runner 'p ENV["OMG"]'), stdout: "1"
|
|
329
|
+
assert_success %(bin/rails runner 'p ENV["BAR"]'), stdout: "bar"
|
|
330
|
+
assert_success %(bin/rails runner 'p ENV.key?("BUNDLE_GEMFILE")'), stdout: "true"
|
|
331
|
+
assert_success %(bin/rails runner 'p ENV["RUBYOPT"]'), stdout: "bundler"
|
|
332
|
+
|
|
333
|
+
app.env["OMG"] = "2"
|
|
334
|
+
app.env.delete "FOO"
|
|
335
|
+
|
|
336
|
+
assert_success %(bin/rails runner 'p ENV["OMG"]'), stdout: "2"
|
|
337
|
+
assert_success %(bin/rails runner 'p ENV.key?("FOO")'), stdout: "false"
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
test "Kernel.raise remains private" do
|
|
341
|
+
expr = "p Kernel.private_instance_methods.include?(:raise)"
|
|
342
|
+
assert_success %(bin/rails runner '#{expr}'), stdout: "true"
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
end
|