spring 0.0.1 → 0.0.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.
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/README.md +195 -15
- data/Rakefile +16 -5
- data/lib/spring/application.rb +9 -3
- data/lib/spring/application_manager.rb +17 -6
- data/lib/spring/commands.rb +6 -2
- data/lib/spring/env.rb +2 -1
- data/lib/spring/server.rb +17 -9
- data/lib/spring/version.rb +1 -1
- data/lib/spring.rb +37 -18
- data/spring.gemspec +1 -0
- data/test/acceptance/app_test.rb +26 -13
- data/test/apps/rails-3-2/config/spring.rb +7 -0
- data/test/unit/application_watcher_test.rb +10 -9
- metadata +21 -3
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.md
CHANGED
|
@@ -1,29 +1,209 @@
|
|
|
1
1
|
# Spring
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Spring is a Rails application preloader. It's trying to solve the same
|
|
4
|
+
problem as [spork](https://github.com/sporkrb/spork),
|
|
5
|
+
[zeus](https://github.com/burke/zeus) and
|
|
6
|
+
[commands](https://github.com/rails/commands).
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
I made it because we are having a discussion on the rails core team
|
|
9
|
+
about shipping something to solve this problem with rails. So this is my
|
|
10
|
+
proposal, as working code.
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
(At least I hope it's working code, but this is alpha software at the
|
|
13
|
+
moment. Please do try it and let me know if you hit problems.)
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
## Features
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
Spring is most similar to Zeus, but it's implemented in pure Ruby, and
|
|
18
|
+
is more tightly integrated with Rails (it makes use of Rails' built-in
|
|
19
|
+
code reloader).
|
|
12
20
|
|
|
13
|
-
|
|
21
|
+
Spring tries to be totally automatic.
|
|
22
|
+
It boots up in the background the first time you run a
|
|
23
|
+
command. Then it speeds up subsequent commands. If it detects that your
|
|
24
|
+
pre-loaded environment has changed (maybe `config/application.rb` has
|
|
25
|
+
been edited) then it will reload your environment in the background,
|
|
26
|
+
ready for the next command. When you close your terminal session, Spring
|
|
27
|
+
will automatically shut down. There's no "server" to manually start and
|
|
28
|
+
stop.
|
|
14
29
|
|
|
15
|
-
|
|
30
|
+
Spring operates via a command line interface. Other solutions (e.g.
|
|
31
|
+
commands) take the approach of using a special console to run commands
|
|
32
|
+
from. This means we will have to re-implement shell features such as
|
|
33
|
+
history, completion, etc. Whilst it's not impossible to re-implement
|
|
34
|
+
those features, it's unnecessary work and our re-implementation
|
|
35
|
+
won't be as feature complete as a real shell. Using a real shell also
|
|
36
|
+
prevents the user having to constantly jump between a terminal with a
|
|
37
|
+
real shell and a terminal running the rails "commands console".
|
|
16
38
|
|
|
17
|
-
|
|
39
|
+
## Compatibility
|
|
40
|
+
|
|
41
|
+
At the moment only MRI 1.9.3 / Rails 3.2 is supported.
|
|
18
42
|
|
|
19
43
|
## Usage
|
|
20
44
|
|
|
21
|
-
|
|
45
|
+
Add `spring` to your gemfile and do a `bundle`.
|
|
46
|
+
|
|
47
|
+
You now have a `spring` command. Do a `rbenv rehash` if necessary. Note
|
|
48
|
+
that on my machine I had over 700 gems installed, and activating the gem
|
|
49
|
+
to run the `spring` command added over 0.5s to the runtime. Clearing out
|
|
50
|
+
my gems solved the problem, but I'd like to figure out a way to speed
|
|
51
|
+
this up.
|
|
52
|
+
|
|
53
|
+
For this walkthrough, I'm using the test app in the Spring repository:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
cd /path/to/spring/test/apps/rails-3-2
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
We can run a test:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
$ time spring test test/functional/posts_controller_test.rb
|
|
63
|
+
Run options:
|
|
64
|
+
|
|
65
|
+
# Running tests:
|
|
66
|
+
|
|
67
|
+
.......
|
|
68
|
+
|
|
69
|
+
Finished tests in 0.169882s, 41.2051 tests/s, 58.8644 assertions/s.
|
|
70
|
+
|
|
71
|
+
7 tests, 10 assertions, 0 failures, 0 errors, 0 skips
|
|
72
|
+
|
|
73
|
+
real 0m1.858s
|
|
74
|
+
user 0m0.184s
|
|
75
|
+
sys 0m0.067s
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
That booted our app in the background:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
$ ps ax | grep spring
|
|
82
|
+
8692 pts/6 Sl 0:00 /home/turnip/.rbenv/versions/1.9.3-p194/bin/ruby -r bundler/setup -r spring/server -e Spring::Server.boot
|
|
83
|
+
8698 pts/6 Sl 0:02 /home/turnip/.rbenv/versions/1.9.3-p194/bin/ruby -r bundler/setup -r spring/server -e Spring::Server.boot
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
We can see two processes, one is the Spring server, the other is the
|
|
87
|
+
application running in the test environment. When we close the terminal,
|
|
88
|
+
the processes will be killed automatically.
|
|
89
|
+
|
|
90
|
+
Running the tests is faster next time:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
$ time spring test test/functional/posts_controller_test.rb
|
|
94
|
+
Run options:
|
|
95
|
+
|
|
96
|
+
# Running tests:
|
|
97
|
+
|
|
98
|
+
.......
|
|
99
|
+
|
|
100
|
+
Finished tests in 0.162963s, 42.9546 tests/s, 61.3637 assertions/s.
|
|
101
|
+
|
|
102
|
+
7 tests, 10 assertions, 0 failures, 0 errors, 0 skips
|
|
103
|
+
|
|
104
|
+
real 0m0.492s
|
|
105
|
+
user 0m0.179s
|
|
106
|
+
sys 0m0.063s
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If we edit any of the application files, or test files, the change will
|
|
110
|
+
be picked up on the next run, without having the background process
|
|
111
|
+
having to be restarted. This works even if you e.g. referenced your
|
|
112
|
+
`Post` model in an initializer and then edited it.
|
|
113
|
+
|
|
114
|
+
If we edit any of the preloaded files, the application needs to restart
|
|
115
|
+
automatically. Note that the application process id is 8698 above. Let's
|
|
116
|
+
"edit" the `config/application.rb`:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
$ touch config/application.rb
|
|
120
|
+
$ ps ax | grep spring
|
|
121
|
+
8692 pts/6 Sl 0:00 /home/turnip/.rbenv/versions/1.9.3-p194/bin/ruby -r bundler/setup -r spring/server -e Spring::Server.boot
|
|
122
|
+
8876 pts/6 Sl 0:00 /home/turnip/.rbenv/versions/1.9.3-p194/bin/ruby -r bundler/setup -r spring/server -e Spring::Server.boot
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The application process detected the change and exited. The server process
|
|
126
|
+
then detected that the application process exited, so it started a new application.
|
|
127
|
+
All of this happens automatically in the background. Next time we run a
|
|
128
|
+
command we'll be running against a fresh application.
|
|
129
|
+
|
|
130
|
+
If we run a command that uses a different environment, then it gets
|
|
131
|
+
booted up. For example, the `rake` command uses the `development`
|
|
132
|
+
environment by default:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
$ time spring rake routes
|
|
136
|
+
posts GET /posts(.:format) posts#index
|
|
137
|
+
POST /posts(.:format) posts#create
|
|
138
|
+
new_post GET /posts/new(.:format) posts#new
|
|
139
|
+
edit_post GET /posts/:id/edit(.:format) posts#edit
|
|
140
|
+
post GET /posts/:id(.:format) posts#show
|
|
141
|
+
PUT /posts/:id(.:format) posts#update
|
|
142
|
+
DELETE /posts/:id(.:format) posts#destroy
|
|
143
|
+
|
|
144
|
+
real 0m0.763s
|
|
145
|
+
user 0m0.185s
|
|
146
|
+
sys 0m0.063s
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
We now have 3 processes: the server, and application in test mode and
|
|
150
|
+
the application in development mode.
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
$ ps ax | grep spring
|
|
154
|
+
8692 pts/6 Sl 0:00 /home/turnip/.rbenv/versions/1.9.3-p194/bin/ruby -r bundler/setup -r spring/server -e Spring::Server.boot
|
|
155
|
+
8876 pts/6 Sl 0:15 /home/turnip/.rbenv/versions/1.9.3-p194/bin/ruby -r bundler/setup -r spring/server -e Spring::Server.boot
|
|
156
|
+
9088 pts/6 Sl 0:01 /home/turnip/.rbenv/versions/1.9.3-p194/bin/ruby -r bundler/setup -r spring/server -e Spring::Server.boot
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Running rake is faster the second time:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
$ time spring rake routes
|
|
163
|
+
posts GET /posts(.:format) posts#index
|
|
164
|
+
POST /posts(.:format) posts#create
|
|
165
|
+
new_post GET /posts/new(.:format) posts#new
|
|
166
|
+
edit_post GET /posts/:id/edit(.:format) posts#edit
|
|
167
|
+
post GET /posts/:id(.:format) posts#show
|
|
168
|
+
PUT /posts/:id(.:format) posts#update
|
|
169
|
+
DELETE /posts/:id(.:format) posts#destroy
|
|
170
|
+
|
|
171
|
+
real 0m0.341s
|
|
172
|
+
user 0m0.177s
|
|
173
|
+
sys 0m0.070s
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Commands
|
|
177
|
+
|
|
178
|
+
The following commands are shipped by default.
|
|
179
|
+
|
|
180
|
+
Custom commands can be specified in `config/spring.rb`. See
|
|
181
|
+
[`lib/spring/commands.rb`](https://github.com/jonleighton/spring/blob/master/lib/spring/commands.rb)
|
|
182
|
+
for examples.
|
|
183
|
+
|
|
184
|
+
### `test`
|
|
185
|
+
|
|
186
|
+
Runs a test (e.g. Test::Unit, MiniTest::Unit, etc.) Preloads the `test_helper` file.
|
|
187
|
+
|
|
188
|
+
### `rspec`
|
|
189
|
+
|
|
190
|
+
Runs an rspec spec, exactly the same as the `rspec` executable. Preloads
|
|
191
|
+
the `spec_helper` file.
|
|
192
|
+
|
|
193
|
+
### `rake`
|
|
194
|
+
|
|
195
|
+
Runs a rake task.
|
|
196
|
+
|
|
197
|
+
### `console`
|
|
198
|
+
|
|
199
|
+
Boots into the Rails console. Currently this is usable but not perfect,
|
|
200
|
+
for example you can't scroll back through command history. (That will be
|
|
201
|
+
fixed.)
|
|
202
|
+
|
|
203
|
+
### `generate`
|
|
204
|
+
|
|
205
|
+
Runs a Rails generator.
|
|
22
206
|
|
|
23
|
-
|
|
207
|
+
### `runner`
|
|
24
208
|
|
|
25
|
-
|
|
26
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
27
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
28
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
|
29
|
-
5. Create new Pull Request
|
|
209
|
+
The Rails runner.
|
data/Rakefile
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
require "bundler/gem_tasks"
|
|
2
2
|
require "rake/testtask"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
namespace :test do
|
|
5
|
+
Rake::TestTask.new(:unit) do |t|
|
|
6
|
+
t.libs << "test"
|
|
7
|
+
t.test_files = FileList["test/unit/*_test.rb"]
|
|
8
|
+
t.verbose = true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Rake::TestTask.new(:acceptance) do |t|
|
|
12
|
+
t.libs << "test"
|
|
13
|
+
t.test_files = FileList["test/acceptance/*_test.rb"]
|
|
14
|
+
t.verbose = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc 'run all tests'
|
|
18
|
+
task all: [:unit, :acceptance]
|
|
8
19
|
end
|
|
9
20
|
|
|
10
|
-
task default: :
|
|
21
|
+
task default: 'test:all'
|
data/lib/spring/application.rb
CHANGED
|
@@ -47,10 +47,16 @@ class Spring
|
|
|
47
47
|
loop do
|
|
48
48
|
watch_application
|
|
49
49
|
|
|
50
|
-
client = manager.recv_io
|
|
51
|
-
client.autoclose = false # prevent GC closing the FD
|
|
50
|
+
client = manager.recv_io(UNIXSocket)
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
# Confirm that we have received the client socket. This is necessary on OS X
|
|
53
|
+
# to prevent a timing error. The client needs to keep the FD that we are receiving
|
|
54
|
+
# open until it has been received here. Unlike on Linux, it's not sufficient for
|
|
55
|
+
# the FD to just be in the socket buffer, it has to actually get received at this
|
|
56
|
+
# end before it can be closed by the client.
|
|
57
|
+
manager.puts
|
|
58
|
+
|
|
59
|
+
serve client
|
|
54
60
|
end
|
|
55
61
|
end
|
|
56
62
|
|
|
@@ -29,20 +29,31 @@ class Spring
|
|
|
29
29
|
@pid
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# The return value of this method indicates whether or not the application
|
|
33
|
+
# successfully received the client. It might not successfully receive the
|
|
34
|
+
# client if e.g. the application has an exception during initialization, causing
|
|
35
|
+
# the application process to die.
|
|
32
36
|
def run(client)
|
|
33
37
|
@client = client
|
|
34
38
|
|
|
35
39
|
synchronize do
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
if alive?
|
|
41
|
+
begin
|
|
42
|
+
child.send_io @client
|
|
43
|
+
rescue Errno::EPIPE
|
|
44
|
+
# EPIPE indicates child has died but has not been collected by the wait thread yet
|
|
45
|
+
start
|
|
46
|
+
child.send_io @client
|
|
47
|
+
end
|
|
48
|
+
else
|
|
42
49
|
start
|
|
43
50
|
child.send_io @client
|
|
44
51
|
end
|
|
52
|
+
|
|
53
|
+
child.gets
|
|
45
54
|
end
|
|
55
|
+
rescue Errno::ECONNRESET, Errno::EPIPE
|
|
56
|
+
false
|
|
46
57
|
ensure
|
|
47
58
|
@client.close
|
|
48
59
|
@client = nil
|
data/lib/spring/commands.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# TODO: Need a way for applications to add their own commands.
|
|
2
|
-
|
|
3
1
|
class Spring
|
|
4
2
|
@commands = {}
|
|
5
3
|
|
|
@@ -15,6 +13,12 @@ class Spring
|
|
|
15
13
|
commands.fetch name
|
|
16
14
|
end
|
|
17
15
|
|
|
16
|
+
# Load custom commands, if any
|
|
17
|
+
begin
|
|
18
|
+
require "./config/spring"
|
|
19
|
+
rescue LoadError
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
module Commands
|
|
19
23
|
class Test
|
|
20
24
|
def env
|
data/lib/spring/env.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "pathname"
|
|
2
2
|
require "spring/sid"
|
|
3
|
+
require "fileutils"
|
|
3
4
|
|
|
4
5
|
class Spring
|
|
5
6
|
class Env
|
|
@@ -11,7 +12,7 @@ class Spring
|
|
|
11
12
|
|
|
12
13
|
def tmp_path
|
|
13
14
|
path = root.join('tmp/spring')
|
|
14
|
-
path
|
|
15
|
+
FileUtils.mkdir_p(path) unless path.exist?
|
|
15
16
|
path
|
|
16
17
|
end
|
|
17
18
|
|
data/lib/spring/server.rb
CHANGED
|
@@ -14,6 +14,7 @@ class Spring
|
|
|
14
14
|
def initialize(env = Env.new)
|
|
15
15
|
@env = env
|
|
16
16
|
@applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k) }
|
|
17
|
+
@pidfile = env.pidfile_path.open('a')
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def boot
|
|
@@ -25,7 +26,18 @@ class Spring
|
|
|
25
26
|
write_pidfile
|
|
26
27
|
|
|
27
28
|
server = UNIXServer.open(env.socket_name)
|
|
28
|
-
loop {
|
|
29
|
+
loop { serve server.accept }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def serve(client)
|
|
33
|
+
app_client = client.recv_io
|
|
34
|
+
rails_env = client.gets.chomp
|
|
35
|
+
|
|
36
|
+
if @applications[rails_env].run(app_client)
|
|
37
|
+
client.write "0"
|
|
38
|
+
else
|
|
39
|
+
client.write "1"
|
|
40
|
+
end
|
|
29
41
|
end
|
|
30
42
|
|
|
31
43
|
def set_exit_hook
|
|
@@ -42,12 +54,10 @@ class Spring
|
|
|
42
54
|
end
|
|
43
55
|
|
|
44
56
|
def write_pidfile
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
file.write("#{Process.pid}\n")
|
|
50
|
-
file.fsync
|
|
57
|
+
if @pidfile.flock(File::LOCK_EX | File::LOCK_NB)
|
|
58
|
+
@pidfile.truncate(0)
|
|
59
|
+
@pidfile.write("#{Process.pid}\n")
|
|
60
|
+
@pidfile.fsync
|
|
51
61
|
else
|
|
52
62
|
STDERR.puts "#{file.path} is locked; it looks like a server is already running"
|
|
53
63
|
exit(1)
|
|
@@ -55,5 +65,3 @@ class Spring
|
|
|
55
65
|
end
|
|
56
66
|
end
|
|
57
67
|
end
|
|
58
|
-
|
|
59
|
-
Spring::Server.boot if __FILE__ == $0
|
data/lib/spring/version.rb
CHANGED
data/lib/spring.rb
CHANGED
|
@@ -12,7 +12,8 @@ class Spring
|
|
|
12
12
|
SERVER_COMMAND = [
|
|
13
13
|
File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')),
|
|
14
14
|
"-r", "bundler/setup",
|
|
15
|
-
|
|
15
|
+
"-r", "spring/server",
|
|
16
|
+
"-e", "Spring::Server.boot"
|
|
16
17
|
]
|
|
17
18
|
|
|
18
19
|
def self.run(args)
|
|
@@ -26,44 +27,62 @@ class Spring
|
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def server_running?
|
|
29
|
-
env.
|
|
30
|
+
if env.pidfile_path.exist?
|
|
31
|
+
pidfile = env.pidfile_path.open('r')
|
|
32
|
+
!pidfile.flock(File::LOCK_EX | File::LOCK_NB)
|
|
33
|
+
else
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
ensure
|
|
37
|
+
if pidfile
|
|
38
|
+
pidfile.flock(File::LOCK_UN)
|
|
39
|
+
pidfile.close
|
|
40
|
+
end
|
|
30
41
|
end
|
|
31
42
|
|
|
43
|
+
# Boot the server into the process group of the current session.
|
|
44
|
+
# This will cause it to be automatically killed once the session
|
|
45
|
+
# ends (i.e. when the user closes their terminal).
|
|
32
46
|
def boot_server
|
|
33
|
-
|
|
34
|
-
# This will cause it to be automatically killed once the session
|
|
35
|
-
# ends (i.e. when the user closes their terminal).
|
|
47
|
+
env.socket_path.unlink if env.socket_path.exist?
|
|
36
48
|
Process.spawn(*SERVER_COMMAND, pgroup: SID.pgid)
|
|
37
|
-
sleep 0.1 until
|
|
49
|
+
sleep 0.1 until env.socket_path.exist?
|
|
38
50
|
end
|
|
39
51
|
|
|
40
52
|
def run(args)
|
|
41
53
|
boot_server unless server_running?
|
|
42
54
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
application, client = UNIXSocket.pair
|
|
56
|
+
|
|
57
|
+
server = UNIXSocket.open(env.socket_name)
|
|
58
|
+
server.send_io client
|
|
59
|
+
server.puts rails_env_for(args.first)
|
|
60
|
+
|
|
61
|
+
status = server.read(1)
|
|
62
|
+
|
|
63
|
+
server.close
|
|
64
|
+
client.close
|
|
46
65
|
|
|
47
|
-
|
|
66
|
+
return false unless status == "0"
|
|
48
67
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
application.send_io STDOUT
|
|
69
|
+
application.send_io STDERR
|
|
70
|
+
application.send_io stdin_slave
|
|
52
71
|
|
|
53
|
-
|
|
72
|
+
application.puts args.length
|
|
54
73
|
|
|
55
74
|
args.each do |arg|
|
|
56
|
-
|
|
57
|
-
|
|
75
|
+
application.puts arg.length
|
|
76
|
+
application.write arg
|
|
58
77
|
end
|
|
59
78
|
|
|
60
79
|
# FIXME: receive exit status from server
|
|
61
|
-
|
|
80
|
+
application.read
|
|
62
81
|
true
|
|
63
82
|
rescue Errno::ECONNRESET
|
|
64
83
|
false
|
|
65
84
|
ensure
|
|
66
|
-
|
|
85
|
+
application.close if application
|
|
67
86
|
end
|
|
68
87
|
|
|
69
88
|
private
|
data/spring.gemspec
CHANGED
data/test/acceptance/app_test.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'helper'
|
|
2
2
|
require 'io/wait'
|
|
3
|
+
require "timeout"
|
|
3
4
|
|
|
4
5
|
class AppTest < ActiveSupport::TestCase
|
|
5
6
|
BINFILE = File.expand_path('../bin/spring', TEST_ROOT)
|
|
@@ -36,20 +37,28 @@ class AppTest < ActiveSupport::TestCase
|
|
|
36
37
|
)
|
|
37
38
|
end
|
|
38
39
|
|
|
39
|
-
_, status = Process.wait2
|
|
40
|
+
_, status = Timeout.timeout(opts.fetch(:timeout, 5)) { Process.wait2 }
|
|
40
41
|
|
|
41
|
-
out, err =
|
|
42
|
+
out, err = read_streams
|
|
42
43
|
|
|
43
44
|
@times << (Time.now - start_time) if opts.fetch(:timer, true)
|
|
44
45
|
|
|
45
|
-
# p status
|
|
46
|
-
# puts "---"
|
|
47
|
-
# puts out
|
|
48
|
-
# puts "***"
|
|
49
|
-
# puts err
|
|
50
|
-
# puts "---"
|
|
51
|
-
|
|
52
46
|
[status, out, err]
|
|
47
|
+
rescue Timeout::Error
|
|
48
|
+
print_streams *read_streams
|
|
49
|
+
raise
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def print_streams(out, err)
|
|
53
|
+
puts "---"
|
|
54
|
+
puts out
|
|
55
|
+
puts "***"
|
|
56
|
+
puts err
|
|
57
|
+
puts "---"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def read_streams
|
|
61
|
+
[stdout, stderr].map(&:first).map { |s| s.ready? ? s.readpartial(10240) : "" }
|
|
53
62
|
end
|
|
54
63
|
|
|
55
64
|
def await_reload
|
|
@@ -58,17 +67,17 @@ class AppTest < ActiveSupport::TestCase
|
|
|
58
67
|
|
|
59
68
|
def assert_successful_run(*args)
|
|
60
69
|
status, _, _ = app_run(*args)
|
|
61
|
-
assert status.success
|
|
70
|
+
assert status.success?, 'The run should be successful but it was '
|
|
62
71
|
end
|
|
63
72
|
|
|
64
73
|
def assert_unsuccessful_run(*args)
|
|
65
74
|
status, _, _ = app_run(*args)
|
|
66
|
-
assert !status.success
|
|
75
|
+
assert !status.success?, 'The run should not be successful but it was'
|
|
67
76
|
end
|
|
68
77
|
|
|
69
78
|
def assert_stdout(command, expected)
|
|
70
79
|
_, stdout, _ = app_run(command)
|
|
71
|
-
assert stdout.include?(expected)
|
|
80
|
+
assert stdout.include?(expected), "expected '#{expected}' to be printed to stdout. But it wasn't, the stdout is:\n#{stdout}"
|
|
72
81
|
end
|
|
73
82
|
|
|
74
83
|
def assert_speedup(opts = {})
|
|
@@ -92,7 +101,7 @@ class AppTest < ActiveSupport::TestCase
|
|
|
92
101
|
|
|
93
102
|
@times = []
|
|
94
103
|
|
|
95
|
-
app_run "bundle check || bundle update", timer: false
|
|
104
|
+
app_run "bundle check || bundle update", timer: false, timeout: nil
|
|
96
105
|
app_run "bundle exec rake db:migrate", timer: false
|
|
97
106
|
end
|
|
98
107
|
|
|
@@ -195,4 +204,8 @@ class AppTest < ActiveSupport::TestCase
|
|
|
195
204
|
File.write(application, application_contents)
|
|
196
205
|
end
|
|
197
206
|
end
|
|
207
|
+
|
|
208
|
+
test "custom commands" do
|
|
209
|
+
assert_stdout "#{BINFILE} custom", "omg"
|
|
210
|
+
end
|
|
198
211
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "helper"
|
|
2
2
|
require "fileutils"
|
|
3
|
+
require "active_support/core_ext/numeric/time"
|
|
3
4
|
require "spring/application_watcher"
|
|
4
5
|
|
|
5
6
|
class ApplicationWatcherTest < Test::Unit::TestCase
|
|
@@ -12,21 +13,21 @@ class ApplicationWatcherTest < Test::Unit::TestCase
|
|
|
12
13
|
FileUtils.rm_r(@dir)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
def touch(file)
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
def touch(file, mtime = nil)
|
|
17
|
+
options = {}
|
|
18
|
+
options[:mtime] = mtime if mtime
|
|
19
|
+
FileUtils.touch(file, options)
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
def test_file_mtime
|
|
21
23
|
file = "#{@dir}/omg"
|
|
22
|
-
touch file
|
|
24
|
+
touch file, Time.now - 2.seconds
|
|
23
25
|
|
|
24
26
|
watcher = Spring::ApplicationWatcher.new
|
|
25
27
|
watcher.add_files [file]
|
|
26
28
|
|
|
27
29
|
assert !watcher.stale?
|
|
28
|
-
|
|
29
|
-
touch file
|
|
30
|
+
touch file, Time.now
|
|
30
31
|
assert watcher.stale?
|
|
31
32
|
end
|
|
32
33
|
|
|
@@ -39,16 +40,16 @@ class ApplicationWatcherTest < Test::Unit::TestCase
|
|
|
39
40
|
|
|
40
41
|
assert !watcher.stale?
|
|
41
42
|
|
|
42
|
-
touch "#{@dir}/1/foo"
|
|
43
|
+
touch "#{@dir}/1/foo", Time.now - 1.minute
|
|
43
44
|
assert !watcher.stale?
|
|
44
45
|
|
|
45
|
-
touch "#{@dir}/1/foo.rb"
|
|
46
|
+
touch "#{@dir}/1/foo.rb", 2.seconds
|
|
46
47
|
assert watcher.stale?
|
|
47
48
|
|
|
48
49
|
watcher.reset
|
|
49
50
|
assert !watcher.stale?
|
|
50
51
|
|
|
51
|
-
touch "#{@dir}/2/foo"
|
|
52
|
+
touch "#{@dir}/2/foo", Time.now
|
|
52
53
|
assert watcher.stale?
|
|
53
54
|
end
|
|
54
55
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spring
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2013-01-11 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activesupport
|
|
@@ -27,6 +27,22 @@ dependencies:
|
|
|
27
27
|
- - ! '>='
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
29
|
version: '0'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: rake
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
type: :development
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
30
46
|
description: Rails application preloader
|
|
31
47
|
email:
|
|
32
48
|
- j@jonathanleighton.com
|
|
@@ -36,6 +52,7 @@ extensions: []
|
|
|
36
52
|
extra_rdoc_files: []
|
|
37
53
|
files:
|
|
38
54
|
- .gitignore
|
|
55
|
+
- .travis.yml
|
|
39
56
|
- Gemfile
|
|
40
57
|
- LICENSE.txt
|
|
41
58
|
- README.md
|
|
@@ -91,6 +108,7 @@ files:
|
|
|
91
108
|
- test/apps/rails-3-2/config/initializers/wrap_parameters.rb
|
|
92
109
|
- test/apps/rails-3-2/config/locales/en.yml
|
|
93
110
|
- test/apps/rails-3-2/config/routes.rb
|
|
111
|
+
- test/apps/rails-3-2/config/spring.rb
|
|
94
112
|
- test/apps/rails-3-2/db/migrate/20121109171227_create_posts.rb
|
|
95
113
|
- test/apps/rails-3-2/db/schema.rb
|
|
96
114
|
- test/apps/rails-3-2/db/seeds.rb
|
|
@@ -184,6 +202,7 @@ test_files:
|
|
|
184
202
|
- test/apps/rails-3-2/config/initializers/wrap_parameters.rb
|
|
185
203
|
- test/apps/rails-3-2/config/locales/en.yml
|
|
186
204
|
- test/apps/rails-3-2/config/routes.rb
|
|
205
|
+
- test/apps/rails-3-2/config/spring.rb
|
|
187
206
|
- test/apps/rails-3-2/db/migrate/20121109171227_create_posts.rb
|
|
188
207
|
- test/apps/rails-3-2/db/schema.rb
|
|
189
208
|
- test/apps/rails-3-2/db/seeds.rb
|
|
@@ -212,4 +231,3 @@ test_files:
|
|
|
212
231
|
- test/apps/rails-3-2/vendor/plugins/.gitkeep
|
|
213
232
|
- test/helper.rb
|
|
214
233
|
- test/unit/application_watcher_test.rb
|
|
215
|
-
has_rdoc:
|