spring-jruby 1.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +364 -0
- data/bin/spring +49 -0
- data/lib/spring-jruby/application.rb +283 -0
- data/lib/spring-jruby/application/boot.rb +19 -0
- data/lib/spring-jruby/binstub.rb +13 -0
- data/lib/spring-jruby/boot.rb +9 -0
- data/lib/spring-jruby/client.rb +46 -0
- data/lib/spring-jruby/client/binstub.rb +188 -0
- data/lib/spring-jruby/client/command.rb +18 -0
- data/lib/spring-jruby/client/help.rb +62 -0
- data/lib/spring-jruby/client/rails.rb +34 -0
- data/lib/spring-jruby/client/run.rb +167 -0
- data/lib/spring-jruby/client/status.rb +30 -0
- data/lib/spring-jruby/client/stop.rb +22 -0
- data/lib/spring-jruby/client/version.rb +11 -0
- data/lib/spring-jruby/command_wrapper.rb +82 -0
- data/lib/spring-jruby/commands.rb +51 -0
- data/lib/spring-jruby/commands/rails.rb +112 -0
- data/lib/spring-jruby/commands/rake.rb +30 -0
- data/lib/spring-jruby/configuration.rb +60 -0
- data/lib/spring-jruby/env.rb +109 -0
- data/lib/spring-jruby/errors.rb +36 -0
- data/lib/spring-jruby/impl/application.rb +7 -0
- data/lib/spring-jruby/impl/application_manager.rb +7 -0
- data/lib/spring-jruby/impl/fork/application.rb +69 -0
- data/lib/spring-jruby/impl/fork/application_manager.rb +137 -0
- data/lib/spring-jruby/impl/fork/run.rb +47 -0
- data/lib/spring-jruby/impl/pool/application.rb +47 -0
- data/lib/spring-jruby/impl/pool/application_manager.rb +226 -0
- data/lib/spring-jruby/impl/pool/run.rb +27 -0
- data/lib/spring-jruby/impl/run.rb +7 -0
- data/lib/spring-jruby/io_helpers.rb +92 -0
- data/lib/spring-jruby/json.rb +626 -0
- data/lib/spring-jruby/platform.rb +23 -0
- data/lib/spring-jruby/process_title_updater.rb +65 -0
- data/lib/spring-jruby/server.rb +130 -0
- data/lib/spring-jruby/sid.rb +42 -0
- data/lib/spring-jruby/test.rb +18 -0
- data/lib/spring-jruby/test/acceptance_test.rb +371 -0
- data/lib/spring-jruby/test/application.rb +217 -0
- data/lib/spring-jruby/test/application_generator.rb +134 -0
- data/lib/spring-jruby/test/rails_version.rb +40 -0
- data/lib/spring-jruby/test/watcher_test.rb +167 -0
- data/lib/spring-jruby/version.rb +3 -0
- data/lib/spring-jruby/watcher.rb +30 -0
- data/lib/spring-jruby/watcher/abstract.rb +86 -0
- data/lib/spring-jruby/watcher/polling.rb +61 -0
- 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
|