nimboids-spork 0.8.99

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/Gemfile +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +129 -0
  4. data/assets/bootstrap.rb +29 -0
  5. data/bin/spork +20 -0
  6. data/ext/mkrf_conf.rb +26 -0
  7. data/features/at_exit_during_each_run.feature +36 -0
  8. data/features/cucumber_rails_integration.feature +107 -0
  9. data/features/diagnostic_mode.feature +41 -0
  10. data/features/gemfiles/rails3.0/Gemfile +10 -0
  11. data/features/gemfiles/rails3.0/Gemfile.lock +116 -0
  12. data/features/rails_delayed_loading_workarounds.feature +150 -0
  13. data/features/rspec_rails_integration.feature +92 -0
  14. data/features/spork_debugger.feature +108 -0
  15. data/features/steps/general_steps.rb +3 -0
  16. data/features/steps/rails_steps.rb +67 -0
  17. data/features/steps/sandbox_steps.rb +115 -0
  18. data/features/support/background_job.rb +63 -0
  19. data/features/support/bundler_helpers.rb +41 -0
  20. data/features/support/env.rb +105 -0
  21. data/features/unknown_app_framework.feature +42 -0
  22. data/lib/spork.rb +155 -0
  23. data/lib/spork/app_framework.rb +80 -0
  24. data/lib/spork/app_framework/padrino.rb +22 -0
  25. data/lib/spork/app_framework/rails.rb +82 -0
  26. data/lib/spork/app_framework/unknown.rb +6 -0
  27. data/lib/spork/custom_io_streams.rb +25 -0
  28. data/lib/spork/diagnoser.rb +105 -0
  29. data/lib/spork/ext/rails-reloader.rb +14 -0
  30. data/lib/spork/ext/ruby-debug.rb +150 -0
  31. data/lib/spork/forker.rb +71 -0
  32. data/lib/spork/run_strategy.rb +44 -0
  33. data/lib/spork/run_strategy/forking.rb +32 -0
  34. data/lib/spork/run_strategy/magazine.rb +141 -0
  35. data/lib/spork/run_strategy/magazine/magazine_slave.rb +30 -0
  36. data/lib/spork/run_strategy/magazine/magazine_slave_provider.rb +27 -0
  37. data/lib/spork/run_strategy/magazine/ring_server.rb +10 -0
  38. data/lib/spork/runner.rb +90 -0
  39. data/lib/spork/server.rb +76 -0
  40. data/lib/spork/test_framework.rb +167 -0
  41. data/lib/spork/test_framework/cucumber.rb +24 -0
  42. data/lib/spork/test_framework/rspec.rb +14 -0
  43. data/spec/spec_helper.rb +113 -0
  44. data/spec/spork/app_framework/rails_spec.rb +22 -0
  45. data/spec/spork/app_framework/unknown_spec.rb +12 -0
  46. data/spec/spork/app_framework_spec.rb +16 -0
  47. data/spec/spork/diagnoser_spec.rb +105 -0
  48. data/spec/spork/forker_spec.rb +44 -0
  49. data/spec/spork/run_strategy/forking_spec.rb +38 -0
  50. data/spec/spork/runner_spec.rb +50 -0
  51. data/spec/spork/server_spec.rb +15 -0
  52. data/spec/spork/test_framework/cucumber_spec.rb +11 -0
  53. data/spec/spork/test_framework/rspec_spec.rb +10 -0
  54. data/spec/spork/test_framework_shared_examples.rb +23 -0
  55. data/spec/spork/test_framework_spec.rb +90 -0
  56. data/spec/spork_spec.rb +153 -0
  57. data/spec/support/fake_framework.rb +15 -0
  58. data/spec/support/fake_run_strategy.rb +21 -0
  59. metadata +158 -0
@@ -0,0 +1,63 @@
1
+ class BackgroundJob
2
+ attr_reader :stdin, :stdout, :stderr, :pid
3
+ def initialize(pid, stdin, stdout, stderr)
4
+ @pid, @stdin, @stdout, @stderr = pid, stdin, stdout, stderr
5
+ ObjectSpace.define_finalizer(self) { kill }
6
+ end
7
+
8
+ def self.run(command)
9
+ command = sanitize_params(command) if command.is_a?(Array)
10
+ child_stdin, parent_stdin = IO::pipe
11
+ parent_stdout, child_stdout = IO::pipe
12
+ parent_stderr, child_stderr = IO::pipe
13
+
14
+ pid = Kernel.fork do
15
+ [parent_stdin, parent_stdout, parent_stderr].each { |io| io.close }
16
+
17
+ STDIN.reopen(child_stdin)
18
+ STDOUT.reopen(child_stdout)
19
+ STDERR.reopen(child_stderr)
20
+
21
+ [child_stdin, child_stdout, child_stderr].each { |io| io.close }
22
+
23
+ exec command
24
+ end
25
+
26
+ [child_stdin, child_stdout, child_stderr].each { |io| io.close }
27
+ parent_stdin.sync = true
28
+
29
+ new(pid, parent_stdin, parent_stdout, parent_stderr)
30
+ end
31
+
32
+ def self.sanitize_params(params)
33
+ params.map { |p| p.gsub(' ', '\ ') }.join(" ")
34
+ end
35
+
36
+ def kill(signal = 'TERM')
37
+ if running?
38
+ Process.kill(Signal.list[signal], @pid)
39
+ true
40
+ end
41
+ end
42
+
43
+ def interrupt
44
+ kill('INT')
45
+ end
46
+
47
+ def running?
48
+ return false unless @pid
49
+ Process.getpgid(@pid)
50
+ true
51
+ rescue Errno::ESRCH
52
+ false
53
+ end
54
+
55
+ def wait(timeout = 1000)
56
+ Timeout.timeout(timeout) do
57
+ Process.wait(@pid)
58
+ end
59
+ true
60
+ rescue Timeout::Error
61
+ false
62
+ end
63
+ end
@@ -0,0 +1,41 @@
1
+ require 'bundler'
2
+ module BundlerHelpers
3
+ extend self
4
+ def install_bundle(dir)
5
+ Dir.chdir(dir) do
6
+ command = "env RUBYOPT= BUNDLE_GEMFILE=Gemfile bundle install"
7
+ system(command)
8
+ $?.exitstatus
9
+ end
10
+ end
11
+
12
+ def ensure_installed(dir)
13
+ gemfile_lock = dir + "/Gemfile.lock"
14
+ gemfile = dir + "/Gemfile"
15
+ bundle_environment = dir + "/.bundle/environment.rb"
16
+ case
17
+ when File.exist?(gemfile_lock) && File.mtime(gemfile) > File.mtime(gemfile_lock)
18
+ puts "Gemfile #{gemfile} has changed since it was locked. Re-locking..."
19
+ FileUtils.rm(gemfile_lock)
20
+ FileUtils.rm_rf(dir + "/.bundle")
21
+ when ! File.exist?(bundle_environment)
22
+ puts "Bundle #{gemfile} not installed. Installing..."
23
+ when File.mtime(bundle_environment) < File.mtime(gemfile_lock)
24
+ puts "#{gemfile_lock} is newer than #{bundle_environment}. Reinstalling"
25
+ else
26
+ return false
27
+ end
28
+ install_bundle(dir)
29
+ end
30
+
31
+ def expand_gemfile(gemfile)
32
+ possibilities = [File.expand_path(gemfile, Dir.pwd), SporkWorld::GEMFILES_ROOT + gemfile + "Gemfile"]
33
+ possibilities.detect {|f| File.exist?(f)} || raise(RuntimeError, %(Gemfile not found:\n #{possibilities * "\n"}))
34
+ end
35
+
36
+ def set_gemfile(gemfile)
37
+ gemfile = expand_gemfile(gemfile || "rails3.0")
38
+ ensure_installed(File.dirname(gemfile))
39
+ ENV["BUNDLE_GEMFILE"] = gemfile
40
+ end
41
+ end
@@ -0,0 +1,105 @@
1
+ require 'rubygems'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ require 'forwardable'
5
+ require 'tempfile'
6
+ require 'rspec/expectations'
7
+ require 'timeout'
8
+
9
+ require(File.dirname(__FILE__) + '/background_job.rb')
10
+
11
+ SPORK_ROOT = Pathname.new(File.expand_path('../../', File.dirname(__FILE__)))
12
+ class SporkWorld
13
+ RUBY_BINARY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
14
+ BINARY = SPORK_ROOT + 'bin/spork'
15
+ SANDBOX_DIR = SPORK_ROOT + "tmp/sandbox"
16
+ GEMFILES_ROOT = SPORK_ROOT + "features/gemfiles"
17
+ SPORK_LIBDIR = SPORK_ROOT + "lib"
18
+
19
+ extend Forwardable
20
+ def_delegators SporkWorld, :sandbox_dir, :spork_lib_dir
21
+
22
+ def spork_lib_dir
23
+ @spork_lib_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
24
+ end
25
+
26
+ def initialize
27
+ @current_dir = SANDBOX_DIR
28
+ @background_jobs = []
29
+ end
30
+
31
+ private
32
+ attr_reader :last_exit_status, :last_stderr, :last_stdout, :background_jobs
33
+ def last_stderr
34
+ return @last_stderr if @last_stderr
35
+ if @background_job
36
+ @last_stderr = @background_job.stderr.read
37
+ end
38
+ end
39
+
40
+
41
+ def last_stdout
42
+ return @last_stdout if @last_stdout
43
+ if @background_job
44
+ @last_stdout = @background_job.stdout.read
45
+ end
46
+ end
47
+
48
+ def create_file(file_name, file_content)
49
+ file_content.gsub!("SPORK_LIB", "'#{spork_lib_dir}'") # Some files, such as Rakefiles need to use the lib dir
50
+ in_current_dir do
51
+ FileUtils.mkdir_p(File.dirname(file_name))
52
+ File.open(file_name, 'w') { |f| f << file_content }
53
+ end
54
+ end
55
+
56
+ def in_current_dir(&block)
57
+ Dir.chdir(@current_dir, &block)
58
+ end
59
+
60
+ def run(command)
61
+ stderr_file = Tempfile.new('spork')
62
+ stderr_file.close
63
+ in_current_dir do
64
+ @last_stdout = `env RUBYOPT= bundle exec #{command} 2> #{stderr_file.path}`
65
+ @last_exit_status = $?.exitstatus
66
+ end
67
+ @last_stderr = IO.read(stderr_file.path)
68
+ end
69
+
70
+ def run_in_background(command)
71
+ in_current_dir do
72
+ @background_job = BackgroundJob.run("env RUBYOPT= bundle exec " + command)
73
+ end
74
+ @background_jobs << @background_job
75
+ @background_job
76
+ end
77
+
78
+ def terminate_background_jobs
79
+ if @background_jobs
80
+ @background_jobs.each do |background_job|
81
+ background_job.kill
82
+ end
83
+ end
84
+ @background_jobs.clear
85
+ @background_job = nil
86
+ end
87
+ end
88
+
89
+ require(SPORK_ROOT + "features/support/bundler_helpers.rb")
90
+ BundlerHelpers.set_gemfile(ENV["GEMFILE"])
91
+
92
+
93
+ World do
94
+ SporkWorld.new
95
+ end
96
+
97
+ Before do
98
+ FileUtils.rm_rf SporkWorld::SANDBOX_DIR
99
+ FileUtils.mkdir_p SporkWorld::SANDBOX_DIR
100
+ end
101
+
102
+ After do
103
+ # FileUtils.rm_rf SporkWorld::SANDBOX_DIR
104
+ terminate_background_jobs
105
+ end
@@ -0,0 +1,42 @@
1
+ Feature: Unknown app frameworks
2
+ To increase to usefulness of Spork
3
+ Spork will work with unknown (or no) application frameworks
4
+
5
+ Scenario: Unsporked spec_helper
6
+
7
+ Given a file named "spec/spec_helper.rb" with:
8
+ """
9
+ require 'rubygems'
10
+ require 'spec'
11
+ """
12
+ When I run spork
13
+ Then the error output should contain "Using RSpec"
14
+ Then the error output should match /You must bootstrap .+spec\/spec_helper\.rb to continue/
15
+
16
+ Scenario: Sporked spec_helper
17
+ Given a file named "spec/spec_helper.rb" with:
18
+ """
19
+ require 'rubygems'
20
+ require 'spork'
21
+
22
+ Spork.prefork do
23
+ require 'spec'
24
+ end
25
+
26
+ Spork.each_run do
27
+ $each_run
28
+ end
29
+ """
30
+ And a file named "spec/did_it_work_spec.rb" with:
31
+ """
32
+ describe "Did it work?" do
33
+ it "checks to see if all worked" do
34
+ Spork.state.should == :using_spork
35
+ puts "Specs successfully run within spork"
36
+ end
37
+ end
38
+ """
39
+ When I fire up a spork instance with "spork rspec"
40
+ And I run spec --drb spec/did_it_work_spec.rb
41
+ Then the output should contain "Specs successfully run within spork"
42
+
@@ -0,0 +1,155 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
2
+ require 'pathname'
3
+ module Spork
4
+ BINARY = File.expand_path(File.dirname(__FILE__) + '/../bin/spork')
5
+ LIBDIR = Pathname.new(File.expand_path(File.dirname(__FILE__)))
6
+
7
+ autoload :Server, (LIBDIR + 'spork/server').to_s
8
+ autoload :TestFramework, (LIBDIR + 'spork/test_framework').to_s
9
+ autoload :AppFramework, (LIBDIR + 'spork/app_framework').to_s
10
+ autoload :RunStrategy, (LIBDIR + 'spork/run_strategy').to_s
11
+ autoload :Runner, (LIBDIR + 'spork/runner').to_s
12
+ autoload :Forker, (LIBDIR + 'spork/forker').to_s
13
+ autoload :Diagnoser, (LIBDIR + 'spork/diagnoser').to_s
14
+
15
+ class << self
16
+ # Run a block, during prefork mode. By default, if prefork is called twice in the same file and line number, the supplied block will only be ran once.
17
+ #
18
+ # == Parameters
19
+ #
20
+ # * +prevent_double_run+ - Pass false to disable double run prevention
21
+ def prefork(prevent_double_run = true, &block)
22
+ return if prevent_double_run && already_ran?(caller.first)
23
+ yield
24
+ end
25
+
26
+ # Run a block AFTER the fork occurs. By default, if prefork is called twice in the same file and line number, the supplied block will only be ran once.
27
+ #
28
+ # == Parameters
29
+ #
30
+ # * +prevent_double_run+ - Pass false to disable double run prevention
31
+ def each_run(prevent_double_run = true, &block)
32
+ return if prevent_double_run && already_ran?(caller.first)
33
+ if state == :prefork
34
+ each_run_procs << block
35
+ else
36
+ yield
37
+ end
38
+ end
39
+
40
+ # Run a block after specs are run.
41
+ #
42
+ # == Parameters
43
+ #
44
+ # * +prevent_double_run+ - Pass false to disable double run prevention
45
+ def after_each_run(prevent_double_run = true, &block)
46
+ return if prevent_double_run && already_ran?(caller.first)
47
+ after_each_run_procs << block
48
+ end
49
+
50
+ def using_spork?
51
+ state != :not_using_spork
52
+ end
53
+
54
+ def state
55
+ @state ||= :not_using_spork
56
+ end
57
+
58
+ # Used by the server. Called when loading the prefork blocks of the code.
59
+ def exec_prefork(&block)
60
+ @state = :prefork
61
+ yield
62
+ end
63
+
64
+ # Used by the server. Called to run all of the prefork blocks.
65
+ def exec_each_run(&block)
66
+ @state = :run
67
+ activate_after_each_run_at_exit_hook
68
+ each_run_procs.each { |p| p.call }
69
+ each_run_procs.clear
70
+ yield if block_given?
71
+ end
72
+
73
+ # Used by the server. Called to run all of the after_each_run blocks.
74
+ def exec_after_each_run
75
+ # processes in reverse order similar to at_exit
76
+ while p = after_each_run_procs.pop; p.call; end
77
+ true
78
+ end
79
+
80
+ # Traps an instance method of a class (or module) so any calls to it don't actually run until Spork.exec_each_run
81
+ def trap_method(klass, method_name)
82
+ method_name_without_spork, method_name_with_spork = alias_method_names(method_name, :spork)
83
+
84
+ klass.class_eval <<-EOF, __FILE__, __LINE__ + 1
85
+ alias :#{method_name_without_spork} :#{method_name} unless method_defined?(:#{method_name_without_spork})
86
+ def #{method_name}(*args, &block)
87
+ Spork.each_run(false) do
88
+ #{method_name_without_spork}(*args, &block)
89
+ end
90
+ end
91
+ EOF
92
+ end
93
+
94
+ # Same as trap_method, but for class methods instead
95
+ def trap_class_method(klass, method_name)
96
+ trap_method((class << klass; self; end), method_name)
97
+ end
98
+
99
+ def detect_and_require(subfolder)
100
+ ([LIBDIR.to_s] + other_spork_gem_load_paths).uniq.each do |gem_path|
101
+ Dir.glob(File.join(gem_path, subfolder)).each { |file| require file }
102
+ end
103
+ end
104
+
105
+ def other_spork_gem_load_paths
106
+ @other_spork_gem_load_paths ||= (
107
+ Gem.latest_load_paths.grep(/spork/).select do |g|
108
+ not g.match(%r{/spork-[0-9\-.]+/lib}) # don't include other versions of spork
109
+ end
110
+ )
111
+ end
112
+
113
+ private
114
+ def activate_after_each_run_at_exit_hook
115
+ Kernel.module_eval do
116
+ def at_exit(&block)
117
+ Spork.after_each_run(false, &block)
118
+ end
119
+ end
120
+ end
121
+
122
+ def alias_method_names(method_name, feature)
123
+ /^(.+?)([\?\!]{0,1})$/.match(method_name.to_s)
124
+ ["#{$1}_without_spork#{$2}", "#{$1}_with_spork#{$2}"]
125
+ end
126
+
127
+ def already_ran
128
+ @already_ran ||= []
129
+ end
130
+
131
+ def expanded_caller(caller_line)
132
+ file, line = caller_line.split(/:(\d+)/)
133
+ line.gsub(/:.+/, '')
134
+ expanded = File.expand_path(file, Dir.pwd) + ":" + line
135
+ if ENV['OS'] == 'Windows_NT' # windows
136
+ expanded = expanded[2..-1]
137
+ end
138
+ expanded
139
+ end
140
+
141
+ def already_ran?(caller_script_and_line)
142
+ return true if already_ran.include?(expanded_caller(caller_script_and_line))
143
+ already_ran << expanded_caller(caller_script_and_line)
144
+ false
145
+ end
146
+
147
+ def each_run_procs
148
+ @each_run_procs ||= []
149
+ end
150
+
151
+ def after_each_run_procs
152
+ @after_each_run_procs ||= []
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,80 @@
1
+ class Spork::AppFramework
2
+ # A hash of procs where the key is the class name, and the proc takes no arguments and returns true if it detects that said application framework is being used in the project.
3
+ #
4
+ # The key :Rails maps to Spork::AppFramework::Rails
5
+ #
6
+ # This is used to reduce the amount of code needed to be loaded - only the detected application framework's support code is loaded.
7
+ SUPPORTED_FRAMEWORKS = {
8
+ :Padrino => lambda {
9
+ File.exist?("config/boot.rb") && File.read("config/boot.rb").include?('PADRINO')
10
+ },
11
+ :Rails => lambda {
12
+ File.exist?("config/environment.rb") && (
13
+ File.read("config/environment.rb").include?('RAILS_GEM_VERSION') ||
14
+ (File.exist?("config/application.rb") && File.read("config/application.rb").include?("Rails::Application"))
15
+ )
16
+ }
17
+ } unless defined? SUPPORTED_FRAMEWORKS
18
+
19
+ def self.setup_autoload
20
+ ([:Unknown] + SUPPORTED_FRAMEWORKS.keys).each do |name|
21
+ autoload name, File.join(File.dirname(__FILE__), "app_framework", name.to_s.downcase)
22
+ end
23
+ end
24
+
25
+ # Iterates through all SUPPORTED_FRAMEWORKS and returns the symbolic name of the project application framework detected. Otherwise, returns :Unknown
26
+ def self.detect_framework_name
27
+ SUPPORTED_FRAMEWORKS.each do |key, value|
28
+ return key if value.call
29
+ end
30
+ :Unknown
31
+ end
32
+
33
+ # Same as detect_framework_name, but returns an instance of the specific AppFramework class.
34
+ def self.detect_framework
35
+ name = detect_framework_name
36
+ self[name]
37
+ end
38
+
39
+ # Initializes, stores, and returns a singleton instance of the named AppFramework.
40
+ #
41
+ # == Parameters
42
+ #
43
+ # # +name+ - A symbolic name of a AppFramework subclass
44
+ #
45
+ # == Example
46
+ #
47
+ # Spork::AppFramework[:Rails]
48
+ def self.[](name)
49
+ instances[name] ||= const_get(name).new
50
+ end
51
+
52
+ def self.short_name
53
+ name.gsub('Spork::AppFramework::', '')
54
+ end
55
+
56
+ # If there is some stuff out of the box that the Spork can do to speed up tests without the test helper file being bootstrapped, this should return false.
57
+ def bootstrap_required?
58
+ entry_point.nil?
59
+ end
60
+
61
+ # Abstract: The path to the file that loads the project environment, ie config/environment.rb. Returns nil if there is none.
62
+ def entry_point
63
+ raise NotImplementedError
64
+ end
65
+
66
+ def preload(&block)
67
+ yield
68
+ end
69
+
70
+ def short_name
71
+ self.class.short_name
72
+ end
73
+
74
+ protected
75
+ def self.instances
76
+ @instances ||= {}
77
+ end
78
+ end
79
+
80
+ Spork::AppFramework.setup_autoload