spring 0.0.8 → 1.0.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 +7 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +113 -0
- data/Gemfile +1 -3
- data/README.md +129 -187
- data/bin/spring +27 -0
- data/lib/spring/application.rb +121 -20
- data/lib/spring/application_manager.rb +21 -13
- data/lib/spring/client/binstub.rb +45 -41
- data/lib/spring/client/help.rb +4 -1
- data/lib/spring/client/rails.rb +8 -3
- data/lib/spring/client/run.rb +33 -10
- data/lib/spring/client/status.rb +2 -2
- data/lib/spring/client.rb +3 -3
- data/lib/spring/commands/rails.rb +60 -0
- data/lib/spring/commands/rake.rb +34 -0
- data/lib/spring/commands.rb +12 -195
- data/lib/spring/configuration.rb +6 -2
- data/lib/spring/env.rb +27 -18
- data/lib/spring/errors.rb +1 -1
- data/lib/spring/json.rb +626 -0
- data/lib/spring/server.rb +60 -29
- data/lib/spring/version.rb +1 -1
- data/lib/spring/watcher/abstract.rb +11 -7
- data/lib/spring/watcher/listen.rb +8 -10
- data/lib/spring/watcher/polling.rb +1 -1
- data/lib/spring/watcher.rb +17 -11
- data/spring.gemspec +1 -0
- data/test/acceptance/app_test.rb +270 -73
- data/test/apps/.gitignore +3 -0
- data/test/unit/client/help_test.rb +2 -0
- data/test/unit/commands_test.rb +8 -49
- data/test/unit/watcher_test.rb +22 -2
- metadata +18 -151
- data/lib/spring/client/start.rb +0 -17
- data/test/apps/rails-3-2/.gitignore +0 -18
- data/test/apps/rails-3-2/Gemfile +0 -9
- data/test/apps/rails-3-2/README.rdoc +0 -261
- data/test/apps/rails-3-2/Rakefile +0 -7
- data/test/apps/rails-3-2/app/assets/images/rails.png +0 -0
- data/test/apps/rails-3-2/app/assets/javascripts/application.js +0 -15
- data/test/apps/rails-3-2/app/assets/javascripts/posts.js.coffee +0 -3
- data/test/apps/rails-3-2/app/assets/stylesheets/application.css +0 -13
- data/test/apps/rails-3-2/app/assets/stylesheets/posts.css.scss +0 -3
- data/test/apps/rails-3-2/app/assets/stylesheets/scaffolds.css.scss +0 -69
- data/test/apps/rails-3-2/app/controllers/application_controller.rb +0 -3
- data/test/apps/rails-3-2/app/controllers/posts_controller.rb +0 -83
- data/test/apps/rails-3-2/app/helpers/application_helper.rb +0 -2
- data/test/apps/rails-3-2/app/helpers/posts_helper.rb +0 -2
- data/test/apps/rails-3-2/app/mailers/.gitkeep +0 -0
- data/test/apps/rails-3-2/app/models/.gitkeep +0 -0
- data/test/apps/rails-3-2/app/models/post.rb +0 -3
- data/test/apps/rails-3-2/app/views/layouts/application.html.erb +0 -14
- data/test/apps/rails-3-2/app/views/posts/_form.html.erb +0 -21
- data/test/apps/rails-3-2/app/views/posts/edit.html.erb +0 -6
- data/test/apps/rails-3-2/app/views/posts/index.html.erb +0 -23
- data/test/apps/rails-3-2/app/views/posts/new.html.erb +0 -5
- data/test/apps/rails-3-2/app/views/posts/show.html.erb +0 -10
- data/test/apps/rails-3-2/config/application.rb +0 -62
- data/test/apps/rails-3-2/config/boot.rb +0 -6
- data/test/apps/rails-3-2/config/database.yml +0 -25
- data/test/apps/rails-3-2/config/environment.rb +0 -5
- data/test/apps/rails-3-2/config/environments/development.rb +0 -37
- data/test/apps/rails-3-2/config/environments/production.rb +0 -67
- data/test/apps/rails-3-2/config/environments/test.rb +0 -37
- data/test/apps/rails-3-2/config/initializers/backtrace_silencers.rb +0 -7
- data/test/apps/rails-3-2/config/initializers/inflections.rb +0 -15
- data/test/apps/rails-3-2/config/initializers/mime_types.rb +0 -5
- data/test/apps/rails-3-2/config/initializers/secret_token.rb +0 -7
- data/test/apps/rails-3-2/config/initializers/session_store.rb +0 -8
- data/test/apps/rails-3-2/config/initializers/wrap_parameters.rb +0 -14
- data/test/apps/rails-3-2/config/locales/en.yml +0 -5
- data/test/apps/rails-3-2/config/routes.rb +0 -60
- data/test/apps/rails-3-2/config/spring.rb +0 -7
- data/test/apps/rails-3-2/config.ru +0 -4
- data/test/apps/rails-3-2/db/migrate/20121109171227_create_posts.rb +0 -9
- data/test/apps/rails-3-2/db/schema.rb +0 -22
- data/test/apps/rails-3-2/db/seeds.rb +0 -7
- data/test/apps/rails-3-2/lib/assets/.gitkeep +0 -0
- data/test/apps/rails-3-2/lib/tasks/.gitkeep +0 -0
- data/test/apps/rails-3-2/log/.gitkeep +0 -0
- data/test/apps/rails-3-2/public/404.html +0 -26
- data/test/apps/rails-3-2/public/422.html +0 -26
- data/test/apps/rails-3-2/public/500.html +0 -25
- data/test/apps/rails-3-2/public/favicon.ico +0 -0
- data/test/apps/rails-3-2/public/index.html +0 -241
- data/test/apps/rails-3-2/public/robots.txt +0 -5
- data/test/apps/rails-3-2/script/rails +0 -6
- data/test/apps/rails-3-2/test/fixtures/.gitkeep +0 -0
- data/test/apps/rails-3-2/test/fixtures/posts.yml +0 -7
- data/test/apps/rails-3-2/test/functional/.gitkeep +0 -0
- data/test/apps/rails-3-2/test/functional/posts_controller_test.rb +0 -49
- data/test/apps/rails-3-2/test/integration/.gitkeep +0 -0
- data/test/apps/rails-3-2/test/performance/browsing_test.rb +0 -12
- data/test/apps/rails-3-2/test/test_helper.rb +0 -13
- data/test/apps/rails-3-2/test/unit/.gitkeep +0 -0
- data/test/apps/rails-3-2/test/unit/helpers/posts_helper_test.rb +0 -4
- data/test/apps/rails-3-2/test/unit/post_test.rb +0 -7
- data/test/apps/rails-3-2/vendor/assets/javascripts/.gitkeep +0 -0
- data/test/apps/rails-3-2/vendor/assets/stylesheets/.gitkeep +0 -0
- data/test/apps/rails-3-2/vendor/plugins/.gitkeep +0 -0
data/lib/spring/server.rb
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
|
+
module Spring
|
|
2
|
+
class << self
|
|
3
|
+
attr_reader :original_env
|
|
4
|
+
end
|
|
5
|
+
@original_env = ENV.to_hash
|
|
6
|
+
end
|
|
7
|
+
|
|
1
8
|
require "socket"
|
|
9
|
+
require "thread"
|
|
2
10
|
|
|
11
|
+
require "spring/configuration"
|
|
3
12
|
require "spring/env"
|
|
4
13
|
require "spring/application_manager"
|
|
5
14
|
require "spring/process_title_updater"
|
|
15
|
+
require "spring/json"
|
|
16
|
+
|
|
17
|
+
# Must be last, as it requires bundler/setup
|
|
6
18
|
require "spring/commands"
|
|
7
19
|
|
|
8
20
|
# readline must be required before we setpgid, otherwise the require may hang,
|
|
@@ -19,47 +31,64 @@ module Spring
|
|
|
19
31
|
|
|
20
32
|
def initialize(env = Env.new)
|
|
21
33
|
@env = env
|
|
22
|
-
@applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k) }
|
|
34
|
+
@applications = Hash.new { |h, k| h[k] = ApplicationManager.new(self, k) }
|
|
23
35
|
@pidfile = env.pidfile_path.open('a')
|
|
36
|
+
@mutex = Mutex.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def log(message)
|
|
40
|
+
env.log "[server] #{message}"
|
|
24
41
|
end
|
|
25
42
|
|
|
26
43
|
def boot
|
|
44
|
+
Spring.verify_environment
|
|
45
|
+
|
|
46
|
+
write_pidfile
|
|
27
47
|
set_pgid
|
|
28
48
|
ignore_signals
|
|
29
49
|
set_exit_hook
|
|
30
|
-
write_pidfile
|
|
31
|
-
redirect_output
|
|
32
50
|
set_process_title
|
|
51
|
+
watch_bundle
|
|
52
|
+
start_server
|
|
53
|
+
end
|
|
33
54
|
|
|
55
|
+
def start_server
|
|
34
56
|
server = UNIXServer.open(env.socket_name)
|
|
57
|
+
log "started on #{env.socket_name}"
|
|
35
58
|
loop { serve server.accept }
|
|
36
59
|
end
|
|
37
60
|
|
|
38
61
|
def serve(client)
|
|
62
|
+
log "accepted client"
|
|
39
63
|
client.puts env.version
|
|
40
64
|
|
|
41
|
-
app_client
|
|
42
|
-
command
|
|
43
|
-
|
|
65
|
+
app_client = client.recv_io
|
|
66
|
+
command = JSON.load(client.read(client.gets.to_i))
|
|
67
|
+
|
|
68
|
+
args, default_rails_env = command.values_at('args', 'default_rails_env')
|
|
44
69
|
|
|
45
70
|
if Spring.command?(args.first)
|
|
71
|
+
log "running command #{args.first}"
|
|
46
72
|
client.puts
|
|
47
|
-
client.puts @applications[rails_env_for(args,
|
|
73
|
+
client.puts @applications[rails_env_for(args, default_rails_env)].run(app_client)
|
|
48
74
|
else
|
|
75
|
+
log "command not found #{args.first}"
|
|
49
76
|
client.close
|
|
50
77
|
end
|
|
51
78
|
rescue SocketError => e
|
|
52
79
|
raise e unless client.eof?
|
|
80
|
+
ensure
|
|
81
|
+
redirect_output
|
|
53
82
|
end
|
|
54
83
|
|
|
55
|
-
def rails_env_for(args,
|
|
84
|
+
def rails_env_for(args, default_rails_env)
|
|
56
85
|
command = Spring.command(args.first)
|
|
57
86
|
|
|
58
87
|
if command.respond_to?(:env)
|
|
59
88
|
env = command.env(args.drop(1))
|
|
60
89
|
end
|
|
61
90
|
|
|
62
|
-
env ||
|
|
91
|
+
env || default_rails_env
|
|
63
92
|
end
|
|
64
93
|
|
|
65
94
|
# Boot the server into the process group of the current session.
|
|
@@ -78,14 +107,16 @@ module Spring
|
|
|
78
107
|
def set_exit_hook
|
|
79
108
|
server_pid = Process.pid
|
|
80
109
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
110
|
+
# We don't want this hook to run in any forks of the current process
|
|
111
|
+
at_exit { shutdown if Process.pid == server_pid }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def shutdown
|
|
115
|
+
@applications.values.each(&:stop)
|
|
87
116
|
|
|
88
|
-
|
|
117
|
+
[env.socket_path, env.pidfile_path].each do |path|
|
|
118
|
+
if path.exist?
|
|
119
|
+
path.unlink rescue nil
|
|
89
120
|
end
|
|
90
121
|
end
|
|
91
122
|
end
|
|
@@ -96,24 +127,16 @@ module Spring
|
|
|
96
127
|
@pidfile.write("#{Process.pid}\n")
|
|
97
128
|
@pidfile.fsync
|
|
98
129
|
else
|
|
99
|
-
$stderr.puts "#{@pidfile.path} is locked; it looks like a server is already running"
|
|
100
130
|
exit 1
|
|
101
131
|
end
|
|
102
132
|
end
|
|
103
133
|
|
|
104
|
-
# We
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
# However we do want server output to go to the terminal in case
|
|
110
|
-
# there are exceptions etc, so we just open the current terminal
|
|
111
|
-
# device directly.
|
|
134
|
+
# We need to redirect STDOUT and STDERR, otherwise the server will
|
|
135
|
+
# keep the original FDs open which would break piping. (e.g.
|
|
136
|
+
# `spring rake -T | grep db` would hang forever because the server
|
|
137
|
+
# would keep the stdout FD open.)
|
|
112
138
|
def redirect_output
|
|
113
|
-
|
|
114
|
-
file = open(STDIN.tty? ? `tty`.chomp : "/dev/null", "a")
|
|
115
|
-
STDOUT.reopen(file)
|
|
116
|
-
STDERR.reopen(file)
|
|
139
|
+
[STDOUT, STDERR].each { |stream| stream.reopen(env.log_file) }
|
|
117
140
|
end
|
|
118
141
|
|
|
119
142
|
def set_process_title
|
|
@@ -121,5 +144,13 @@ module Spring
|
|
|
121
144
|
"spring server | #{env.app_name} | started #{distance} ago"
|
|
122
145
|
}
|
|
123
146
|
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
|
|
124
155
|
end
|
|
125
156
|
end
|
data/lib/spring/version.rb
CHANGED
|
@@ -36,15 +36,19 @@ module Spring
|
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
items
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
items = items.select(&:exist?)
|
|
40
|
+
|
|
41
|
+
synchronize {
|
|
42
|
+
items.each do |item|
|
|
43
|
+
if item.directory?
|
|
44
|
+
directories << item.realpath.to_s
|
|
45
|
+
else
|
|
46
|
+
files << item.realpath.to_s
|
|
47
|
+
end
|
|
44
48
|
end
|
|
45
|
-
end
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
subjects_changed
|
|
51
|
+
}
|
|
48
52
|
end
|
|
49
53
|
|
|
50
54
|
def stale?
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
+
gem "listen", "~> 1.0"
|
|
2
|
+
require "listen"
|
|
3
|
+
require "listen/version"
|
|
4
|
+
|
|
1
5
|
module Spring
|
|
2
6
|
module Watcher
|
|
3
7
|
class Listen < Abstract
|
|
4
8
|
attr_reader :listener
|
|
5
9
|
|
|
6
|
-
def self.available?
|
|
7
|
-
require "listen"
|
|
8
|
-
true
|
|
9
|
-
rescue LoadError
|
|
10
|
-
false
|
|
11
|
-
end
|
|
12
|
-
|
|
13
10
|
def start
|
|
14
11
|
unless @listener
|
|
15
|
-
@listener = ::Listen
|
|
12
|
+
@listener = ::Listen.to(*base_directories, relative_paths: false)
|
|
16
13
|
@listener.latency(latency)
|
|
17
14
|
@listener.change(&method(:changed))
|
|
18
|
-
@listener.start
|
|
15
|
+
@listener.start
|
|
19
16
|
end
|
|
20
17
|
end
|
|
21
18
|
|
|
@@ -45,9 +42,10 @@ module Spring
|
|
|
45
42
|
end
|
|
46
43
|
|
|
47
44
|
def base_directories
|
|
48
|
-
[root] +
|
|
45
|
+
([root] +
|
|
49
46
|
files.reject { |f| f.start_with? root }.map { |f| File.expand_path("#{f}/..") } +
|
|
50
47
|
directories.reject { |d| d.start_with? root }
|
|
48
|
+
).uniq
|
|
51
49
|
end
|
|
52
50
|
end
|
|
53
51
|
end
|
data/lib/spring/watcher.rb
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
require "spring/watcher/abstract"
|
|
2
|
-
require "spring/
|
|
3
|
-
require "spring/watcher/polling"
|
|
2
|
+
require "spring/configuration"
|
|
4
3
|
|
|
5
4
|
module Spring
|
|
6
5
|
class << self
|
|
7
6
|
attr_accessor :watch_interval
|
|
8
7
|
attr_writer :watcher
|
|
8
|
+
attr_reader :watch_method
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.watch_method=(method)
|
|
12
|
+
case method
|
|
13
|
+
when :polling
|
|
14
|
+
require_relative "watcher/polling"
|
|
15
|
+
@watch_method = Watcher::Polling
|
|
16
|
+
when :listen
|
|
17
|
+
require_relative "watcher/listen"
|
|
18
|
+
@watch_method = Watcher::Listen
|
|
19
|
+
else
|
|
20
|
+
@watch_method = method
|
|
21
|
+
end
|
|
9
22
|
end
|
|
10
23
|
|
|
11
24
|
self.watch_interval = 0.2
|
|
25
|
+
self.watch_method = :polling
|
|
12
26
|
|
|
13
27
|
def self.watcher
|
|
14
|
-
@watcher ||=
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def self.watcher_class
|
|
18
|
-
if Watcher::Listen.available?
|
|
19
|
-
Watcher::Listen
|
|
20
|
-
else
|
|
21
|
-
Watcher::Polling
|
|
22
|
-
end
|
|
28
|
+
@watcher ||= watch_method.new(Spring.application_root_path, watch_interval)
|
|
23
29
|
end
|
|
24
30
|
|
|
25
31
|
def self.watch(*items)
|
data/spring.gemspec
CHANGED
|
@@ -11,6 +11,7 @@ Gem::Specification.new do |gem|
|
|
|
11
11
|
gem.description = %q{Rails application preloader}
|
|
12
12
|
gem.summary = %q{Rails application preloader}
|
|
13
13
|
gem.homepage = "http://github.com/jonleighton/spring"
|
|
14
|
+
gem.license = "MIT"
|
|
14
15
|
|
|
15
16
|
gem.files = `git ls-files`.split($/)
|
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|