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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +364 -0
  4. data/bin/spring +49 -0
  5. data/lib/spring-jruby/application.rb +283 -0
  6. data/lib/spring-jruby/application/boot.rb +19 -0
  7. data/lib/spring-jruby/binstub.rb +13 -0
  8. data/lib/spring-jruby/boot.rb +9 -0
  9. data/lib/spring-jruby/client.rb +46 -0
  10. data/lib/spring-jruby/client/binstub.rb +188 -0
  11. data/lib/spring-jruby/client/command.rb +18 -0
  12. data/lib/spring-jruby/client/help.rb +62 -0
  13. data/lib/spring-jruby/client/rails.rb +34 -0
  14. data/lib/spring-jruby/client/run.rb +167 -0
  15. data/lib/spring-jruby/client/status.rb +30 -0
  16. data/lib/spring-jruby/client/stop.rb +22 -0
  17. data/lib/spring-jruby/client/version.rb +11 -0
  18. data/lib/spring-jruby/command_wrapper.rb +82 -0
  19. data/lib/spring-jruby/commands.rb +51 -0
  20. data/lib/spring-jruby/commands/rails.rb +112 -0
  21. data/lib/spring-jruby/commands/rake.rb +30 -0
  22. data/lib/spring-jruby/configuration.rb +60 -0
  23. data/lib/spring-jruby/env.rb +109 -0
  24. data/lib/spring-jruby/errors.rb +36 -0
  25. data/lib/spring-jruby/impl/application.rb +7 -0
  26. data/lib/spring-jruby/impl/application_manager.rb +7 -0
  27. data/lib/spring-jruby/impl/fork/application.rb +69 -0
  28. data/lib/spring-jruby/impl/fork/application_manager.rb +137 -0
  29. data/lib/spring-jruby/impl/fork/run.rb +47 -0
  30. data/lib/spring-jruby/impl/pool/application.rb +47 -0
  31. data/lib/spring-jruby/impl/pool/application_manager.rb +226 -0
  32. data/lib/spring-jruby/impl/pool/run.rb +27 -0
  33. data/lib/spring-jruby/impl/run.rb +7 -0
  34. data/lib/spring-jruby/io_helpers.rb +92 -0
  35. data/lib/spring-jruby/json.rb +626 -0
  36. data/lib/spring-jruby/platform.rb +23 -0
  37. data/lib/spring-jruby/process_title_updater.rb +65 -0
  38. data/lib/spring-jruby/server.rb +130 -0
  39. data/lib/spring-jruby/sid.rb +42 -0
  40. data/lib/spring-jruby/test.rb +18 -0
  41. data/lib/spring-jruby/test/acceptance_test.rb +371 -0
  42. data/lib/spring-jruby/test/application.rb +217 -0
  43. data/lib/spring-jruby/test/application_generator.rb +134 -0
  44. data/lib/spring-jruby/test/rails_version.rb +40 -0
  45. data/lib/spring-jruby/test/watcher_test.rb +167 -0
  46. data/lib/spring-jruby/version.rb +3 -0
  47. data/lib/spring-jruby/watcher.rb +30 -0
  48. data/lib/spring-jruby/watcher/abstract.rb +86 -0
  49. data/lib/spring-jruby/watcher/polling.rb +61 -0
  50. metadata +137 -0
@@ -0,0 +1,217 @@
1
+ require "spring-jruby/env"
2
+
3
+ module Spring
4
+ module Test
5
+ class Application
6
+ DEFAULT_TIMEOUT = ENV['CI'] ? 30 : 10
7
+
8
+ attr_reader :root, :spring_env
9
+
10
+ def initialize(root)
11
+ @root = Pathname.new(root)
12
+ @spring_env = Spring::Env.new(root)
13
+ end
14
+
15
+ def exists?
16
+ root.exist?
17
+ end
18
+
19
+ def stdout
20
+ @stdout ||= IO.pipe
21
+ end
22
+
23
+ def stderr
24
+ @stderr ||= IO.pipe
25
+ end
26
+
27
+ def log_file
28
+ @log_file ||= path("tmp/spring.log").open("w+")
29
+ end
30
+
31
+ def env
32
+ @env ||= {
33
+ "GEM_HOME" => gem_home.to_s,
34
+ "GEM_PATH" => gem_home.to_s,
35
+ "HOME" => user_home.to_s,
36
+ "RAILS_ENV" => nil,
37
+ "RACK_ENV" => nil,
38
+ "SPRING_LOG" => log_file.path
39
+ }
40
+ end
41
+
42
+ def path(addition)
43
+ root.join addition
44
+ end
45
+
46
+ def gemfile
47
+ path "Gemfile"
48
+ end
49
+
50
+ def gem_home
51
+ path "../gems/#{RUBY_VERSION}"
52
+ end
53
+
54
+ def user_home
55
+ path "user_home"
56
+ end
57
+
58
+ def spring
59
+ gem_home.join "bin/spring"
60
+ end
61
+
62
+ def rails_version
63
+ @rails_version ||= RailsVersion.new(gemfile.read.match(/gem 'rails', '(.*)'/)[1])
64
+ end
65
+
66
+ def spring_test_command
67
+ "#{rails_version.test_command} #{test}"
68
+ end
69
+
70
+ def stop_spring
71
+ run "#{spring} stop"
72
+ rescue Errno::ENOENT
73
+ end
74
+
75
+ def test
76
+ path "test/#{rails_version.controller_tests_dir}/posts_controller_test.rb"
77
+ end
78
+
79
+ def controller
80
+ path "app/controllers/posts_controller.rb"
81
+ end
82
+
83
+ def application_config
84
+ path "config/application.rb"
85
+ end
86
+
87
+ def spring_config
88
+ path "config/spring.rb"
89
+ end
90
+
91
+ def run(command, opts = {})
92
+ start_time = Time.now
93
+
94
+ Bundler.with_clean_env do
95
+ Process.spawn(
96
+ env,
97
+ command.to_s,
98
+ out: stdout.last,
99
+ err: stderr.last,
100
+ in: :close,
101
+ chdir: root.to_s,
102
+ )
103
+ end
104
+
105
+ _, status = Timeout.timeout(opts.fetch(:timeout, DEFAULT_TIMEOUT)) { Process.wait2 }
106
+
107
+ if pid = spring_env.pid
108
+ @server_pid = pid
109
+ lines = `ps -A -o ppid= -o pid= | egrep '^\\s*#{@server_pid}'`.lines
110
+ @application_pids = lines.map { |l| l.split.last.to_i }
111
+ end
112
+
113
+ output = read_streams
114
+ puts dump_streams(command, output) if ENV["SPRING_DEBUG"]
115
+
116
+ @times << (Time.now - start_time) if @times
117
+
118
+ output.merge(status: status, command: command)
119
+ rescue Timeout::Error => e
120
+ raise e, "Output:\n\n#{dump_streams(command, read_streams)}"
121
+ end
122
+
123
+ def with_timing
124
+ @times = []
125
+ yield
126
+ ensure
127
+ @times = nil
128
+ end
129
+
130
+ def last_time
131
+ @times.last
132
+ end
133
+
134
+ def first_time
135
+ @times.first
136
+ end
137
+
138
+ def timing_ratio
139
+ last_time / first_time
140
+ end
141
+
142
+ def read_streams
143
+ {
144
+ stdout: read_stream(stdout.first),
145
+ stderr: read_stream(stderr.first),
146
+ log: read_stream(log_file)
147
+ }
148
+ end
149
+
150
+ def read_stream(stream)
151
+ output = ""
152
+ while IO.select([stream], [], [], 0.5) && !stream.eof?
153
+ output << stream.readpartial(10240)
154
+ end
155
+ output
156
+ end
157
+
158
+ def dump_streams(command, streams)
159
+ output = "$ #{command}\n"
160
+
161
+ streams.each do |name, stream|
162
+ unless stream.chomp.empty?
163
+ output << "--- #{name} ---\n"
164
+ output << "#{stream.chomp}\n"
165
+ end
166
+ end
167
+
168
+ output << "\n"
169
+ output
170
+ end
171
+
172
+ def debug(artifacts)
173
+ artifacts = artifacts.dup
174
+ artifacts.delete :status
175
+ dump_streams(artifacts.delete(:command), artifacts)
176
+ end
177
+
178
+ def await_reload
179
+ raise "no pid" if @application_pids.nil? || @application_pids.empty?
180
+
181
+ Timeout.timeout(DEFAULT_TIMEOUT) do
182
+ sleep 0.1 while @application_pids.any? { |p| process_alive?(p) }
183
+ end
184
+ end
185
+
186
+ def run!(command, options = {})
187
+ attempts = (options.delete(:retry) || 0) + 1
188
+ artifacts = nil
189
+
190
+ until attempts == 0 || artifacts && artifacts[:status].success?
191
+ artifacts = run(command, options)
192
+ attempts -= 1
193
+ end
194
+
195
+ if artifacts[:status].success?
196
+ artifacts
197
+ else
198
+ raise "command failed\n\n#{debug(artifacts)}"
199
+ end
200
+ end
201
+
202
+ def bundle
203
+ run! "(gem list bundler | grep bundler) || gem install bundler", timeout: nil, retry: 2
204
+ run! "bundle check || bundle update --retry=2", timeout: nil
205
+ end
206
+
207
+ private
208
+
209
+ def process_alive?(pid)
210
+ Process.kill 0, pid
211
+ true
212
+ rescue Errno::ESRCH
213
+ false
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,134 @@
1
+ module Spring
2
+ module Test
3
+ class ApplicationGenerator
4
+ attr_reader :version_constraint, :version, :application
5
+
6
+ def initialize(version_constraint)
7
+ @version_constraint = version_constraint
8
+ @version = RailsVersion.new(version_constraint.split(' ').last)
9
+ @application = Application.new(root)
10
+ @bundled = false
11
+ end
12
+
13
+ def test_root
14
+ Pathname.new Spring::Test.root
15
+ end
16
+
17
+ def root
18
+ test_root.join("apps/rails-#{version.major}-#{version.minor}-spring-#{Spring::VERSION}")
19
+ end
20
+
21
+ def system(command)
22
+ if ENV["SPRING_DEBUG"]
23
+ puts "$ #{command}\n"
24
+ else
25
+ command = "(#{command}) > /dev/null"
26
+ end
27
+
28
+ Kernel.system(command) or raise "command failed: #{command}"
29
+ puts if ENV["SPRING_DEBUG"]
30
+ end
31
+
32
+ def generate
33
+ Bundler.with_clean_env { generate_files }
34
+ install_spring
35
+ generate_scaffold
36
+ end
37
+
38
+ # Sporadic SSL errors keep causing test failures so there are anti-SSL workarounds here
39
+ def generate_files
40
+ system("gem list '^rails$' --installed --version '#{version_constraint}' || " \
41
+ "gem install rails --clear-sources --source http://rubygems.org --version '#{version_constraint}'")
42
+
43
+ @version = RailsVersion.new(`ruby -e 'puts Gem::Specification.find_by_name("rails", "#{version_constraint}").version'`.chomp)
44
+
45
+ skips = %w(--skip-bundle --skip-javascript --skip-sprockets)
46
+ skips << "--skip-spring" if version.bundles_spring?
47
+
48
+ system("rails _#{version}_ new #{application.root} #{skips.join(' ')}")
49
+ raise "application generation failed" unless application.exists?
50
+
51
+ FileUtils.mkdir_p(application.gem_home)
52
+ FileUtils.mkdir_p(application.user_home)
53
+ FileUtils.rm_rf(application.path("test/performance"))
54
+
55
+ append_to_file(application.gemfile, "gem 'spring', '#{Spring::VERSION}'")
56
+
57
+ if version.needs_testunit?
58
+ append_to_file(application.gemfile, "gem 'spring-commands-testunit'")
59
+ end
60
+
61
+ rewrite_file(application.gemfile) do |c|
62
+ c.sub!("https://rubygems.org", "http://rubygems.org")
63
+ c.gsub!(/(gem '(byebug|web-console|sdoc|jbuilder)')/, "# \\1")
64
+ c
65
+ end
66
+
67
+
68
+ if application.path("bin").exist?
69
+ FileUtils.cp_r(application.path("bin"), application.path("bin_original"))
70
+ end
71
+ end
72
+
73
+ def rewrite_file(file)
74
+ File.write(file, yield(file.read))
75
+ end
76
+
77
+ def append_to_file(file, add)
78
+ rewrite_file(file) { |c| c << "#{add}\n" }
79
+ end
80
+
81
+ def generate_if_missing
82
+ generate unless application.exists?
83
+ end
84
+
85
+ def install_spring
86
+ return if @installed
87
+
88
+ build_and_install_gems
89
+
90
+ application.bundle
91
+
92
+ FileUtils.rm_rf application.path("bin")
93
+
94
+ if application.path("bin_original").exist?
95
+ FileUtils.cp_r application.path("bin_original"), application.path("bin")
96
+ end
97
+
98
+ application.run! "#{application.spring} binstub --all"
99
+ @installed = true
100
+ end
101
+
102
+ def manually_built_gems
103
+ %w(spring)
104
+ end
105
+
106
+ def build_and_install_gems
107
+ manually_built_gems.each do |name|
108
+ spec = Gem::Specification.find_by_name(name)
109
+
110
+ FileUtils.cd(spec.gem_dir) do
111
+ FileUtils.rm(Dir.glob("#{name}-*.gem"))
112
+ system("gem build #{name}.gemspec 2>&1")
113
+ end
114
+
115
+ application.run! "gem install #{spec.gem_dir}/#{name}-*.gem", timeout: nil
116
+ end
117
+ end
118
+
119
+ def copy_to(path)
120
+ system("rm -rf #{path}")
121
+ system("cp -r #{application.root} #{path}")
122
+ end
123
+
124
+ def generate_scaffold
125
+ application.run! "bundle exec rails g scaffold post title:string"
126
+ application.run! "bundle exec rake db:migrate db:test:clone"
127
+ end
128
+
129
+ def gemspec(name)
130
+ "#{Gem::Specification.find_by_name(name).gem_dir}/#{name}.gemspec"
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,40 @@
1
+ module Spring
2
+ module Test
3
+ class RailsVersion
4
+ attr_reader :version
5
+
6
+ def initialize(string)
7
+ @version = Gem::Version.new(string)
8
+ end
9
+
10
+ def rails_3?
11
+ version < Gem::Version.new("4.0.0")
12
+ end
13
+ alias needs_testunit? rails_3?
14
+
15
+ def test_command
16
+ needs_testunit? ? 'bin/testunit' : 'bin/rake test'
17
+ end
18
+
19
+ def controller_tests_dir
20
+ rails_3? ? 'functional' : 'controllers'
21
+ end
22
+
23
+ def bundles_spring?
24
+ version.segments.take(2) == [4, 1] || version > Gem::Version.new("4.1")
25
+ end
26
+
27
+ def major
28
+ version.segments[0]
29
+ end
30
+
31
+ def minor
32
+ version.segments[1]
33
+ end
34
+
35
+ def to_s
36
+ version.to_s
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,167 @@
1
+ require "tmpdir"
2
+ require "fileutils"
3
+ require "timeout"
4
+ require "active_support/core_ext/numeric/time"
5
+
6
+ module Spring
7
+ module Test
8
+ class WatcherTest < ActiveSupport::TestCase
9
+ runnables.delete self # prevent Minitest running this class
10
+
11
+ LATENCY = 0.001
12
+ TIMEOUT = 1
13
+
14
+ attr_accessor :dir
15
+
16
+ def watcher_class
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def watcher
21
+ @watcher ||= watcher_class.new(dir, LATENCY)
22
+ end
23
+
24
+ def setup
25
+ @dir = File.realpath(Dir.mktmpdir)
26
+ end
27
+
28
+ def teardown
29
+ FileUtils.remove_entry_secure @dir
30
+ watcher.stop
31
+ end
32
+
33
+ def touch(file, mtime = nil)
34
+ options = {}
35
+ options[:mtime] = mtime if mtime
36
+ FileUtils.touch(file, options)
37
+ end
38
+
39
+ def assert_stale
40
+ timeout = Time.now + TIMEOUT
41
+ sleep LATENCY until watcher.stale? || Time.now > timeout
42
+ assert watcher.stale?
43
+ end
44
+
45
+ def assert_not_stale
46
+ sleep LATENCY * 10
47
+ assert !watcher.stale?
48
+ end
49
+
50
+ test "starting with no file" do
51
+ file = "#{@dir}/omg"
52
+ touch file, Time.now - 2.seconds
53
+
54
+ watcher.start
55
+ watcher.add file
56
+
57
+ assert_not_stale
58
+ touch file, Time.now
59
+ assert_stale
60
+ end
61
+
62
+ test "is stale when a watched file is updated" do
63
+ file = "#{@dir}/omg"
64
+ touch file, Time.now - 2.seconds
65
+
66
+ watcher.add file
67
+ watcher.start
68
+
69
+ assert_not_stale
70
+ touch file, Time.now
71
+ assert_stale
72
+ end
73
+
74
+ test "is stale when removing files" do
75
+ file = "#{@dir}/omg"
76
+ touch file, Time.now
77
+
78
+ watcher.add file
79
+ watcher.start
80
+
81
+ assert_not_stale
82
+ FileUtils.rm(file)
83
+ assert_stale
84
+ end
85
+
86
+ test "is stale when files are added to a watched directory" do
87
+ subdir = "#{@dir}/subdir"
88
+ FileUtils.mkdir(subdir)
89
+
90
+ watcher.add subdir
91
+ watcher.start
92
+
93
+ assert_not_stale
94
+ touch "#{subdir}/foo", Time.now - 1.minute
95
+ assert_stale
96
+ end
97
+
98
+ test "is stale when a file is changed in a watched directory" do
99
+ subdir = "#{@dir}/subdir"
100
+ FileUtils.mkdir(subdir)
101
+ touch "#{subdir}/foo", Time.now - 1.minute
102
+
103
+ watcher.add subdir
104
+ watcher.start
105
+
106
+ assert_not_stale
107
+ touch "#{subdir}/foo", Time.now
108
+ assert_stale
109
+ end
110
+
111
+ test "adding doesn't wipe stale state" do
112
+ file = "#{@dir}/omg"
113
+ file2 = "#{@dir}/foo"
114
+ touch file, Time.now - 2.seconds
115
+ touch file2, Time.now - 2.seconds
116
+
117
+ watcher.add file
118
+ watcher.start
119
+
120
+ assert_not_stale
121
+
122
+ touch file, Time.now
123
+ watcher.add file2
124
+
125
+ assert_stale
126
+ end
127
+
128
+ test "on stale" do
129
+ file = "#{@dir}/omg"
130
+ touch file, Time.now - 2.seconds
131
+
132
+ stale = false
133
+ watcher.on_stale { stale = true }
134
+
135
+ watcher.add file
136
+ watcher.start
137
+
138
+ touch file, Time.now
139
+
140
+ Timeout.timeout(1) { sleep 0.01 until stale }
141
+ assert stale
142
+
143
+ # Check that we only get notified once
144
+ stale = false
145
+ sleep LATENCY * 3
146
+ assert !stale
147
+ end
148
+
149
+ test "add relative path" do
150
+ File.write("#{dir}/foo", "foo")
151
+ watcher.add "foo"
152
+ assert_equal ["#{dir}/foo"], watcher.files.to_a
153
+ end
154
+
155
+ test "add dot relative path" do
156
+ File.write("#{dir}/foo", "foo")
157
+ watcher.add "./foo"
158
+ assert_equal ["#{dir}/foo"], watcher.files.to_a
159
+ end
160
+
161
+ test "add non existent file" do
162
+ watcher.add './foobar'
163
+ assert watcher.files.empty?
164
+ end
165
+ end
166
+ end
167
+ end