spring-jruby 1.4.3
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 +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
|