wakiki-spork 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/Gemfile +6 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +127 -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 +35 -0
  8. data/features/cucumber_rails_integration.feature +111 -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 +135 -0
  12. data/features/rails_delayed_loading_workarounds.feature +115 -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 +63 -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 +42 -0
  20. data/features/support/env.rb +117 -0
  21. data/features/unknown_app_framework.feature +42 -0
  22. data/lib/spork.rb +156 -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 +167 -0
  26. data/lib/spork/app_framework/rails_stub_files/application.rb +1 -0
  27. data/lib/spork/app_framework/rails_stub_files/application_controller.rb +22 -0
  28. data/lib/spork/app_framework/rails_stub_files/application_helper.rb +3 -0
  29. data/lib/spork/app_framework/unknown.rb +6 -0
  30. data/lib/spork/custom_io_streams.rb +25 -0
  31. data/lib/spork/diagnoser.rb +105 -0
  32. data/lib/spork/ext/rails-reloader.rb +14 -0
  33. data/lib/spork/ext/ruby-debug.rb +150 -0
  34. data/lib/spork/forker.rb +71 -0
  35. data/lib/spork/run_strategy.rb +44 -0
  36. data/lib/spork/run_strategy/forking.rb +32 -0
  37. data/lib/spork/run_strategy/magazine.rb +121 -0
  38. data/lib/spork/run_strategy/magazine/magazine_slave.rb +30 -0
  39. data/lib/spork/run_strategy/magazine/magazine_slave_provider.rb +27 -0
  40. data/lib/spork/run_strategy/magazine/ring_server.rb +10 -0
  41. data/lib/spork/runner.rb +91 -0
  42. data/lib/spork/server.rb +74 -0
  43. data/lib/spork/test_framework.rb +167 -0
  44. data/lib/spork/test_framework/cucumber.rb +24 -0
  45. data/lib/spork/test_framework/rspec.rb +14 -0
  46. data/spec/spec_helper.rb +108 -0
  47. data/spec/spork/app_framework/rails_spec.rb +22 -0
  48. data/spec/spork/app_framework/unknown_spec.rb +12 -0
  49. data/spec/spork/app_framework_spec.rb +16 -0
  50. data/spec/spork/diagnoser_spec.rb +105 -0
  51. data/spec/spork/forker_spec.rb +44 -0
  52. data/spec/spork/run_strategy/forking_spec.rb +38 -0
  53. data/spec/spork/runner_spec.rb +50 -0
  54. data/spec/spork/server_spec.rb +15 -0
  55. data/spec/spork/test_framework/cucumber_spec.rb +11 -0
  56. data/spec/spork/test_framework/rspec_spec.rb +10 -0
  57. data/spec/spork/test_framework_spec.rb +114 -0
  58. data/spec/spork_spec.rb +151 -0
  59. data/spec/support/fake_framework.rb +15 -0
  60. data/spec/support/fake_run_strategy.rb +21 -0
  61. metadata +159 -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,42 @@
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
+ command << " --relock" unless File.exist?("Gemfile.lock")
8
+ system(command)
9
+ $?.exitstatus
10
+ end
11
+ end
12
+
13
+ def ensure_installed(dir)
14
+ gemfile_lock = dir + "/Gemfile.lock"
15
+ gemfile = dir + "/Gemfile"
16
+ bundle_environment = dir + "/.bundle/environment.rb"
17
+ case
18
+ when File.exist?(gemfile_lock) && File.mtime(gemfile) > File.mtime(gemfile_lock)
19
+ puts "Gemfile #{gemfile} has changed since it was locked. Re-locking..."
20
+ FileUtils.rm(gemfile_lock)
21
+ FileUtils.rm_rf(dir + "/.bundle")
22
+ when ! File.exist?(bundle_environment)
23
+ puts "Bundle #{gemfile} not installed. Installing..."
24
+ when File.mtime(bundle_environment) < File.mtime(gemfile_lock)
25
+ puts "#{gemfile_lock} is newer than #{bundle_environment}. Reinstalling"
26
+ else
27
+ return false
28
+ end
29
+ install_bundle(dir)
30
+ end
31
+
32
+ def expand_gemfile(gemfile)
33
+ possibilities = [File.expand_path(gemfile, Dir.pwd), SporkWorld::GEMFILES_ROOT + gemfile + "Gemfile"]
34
+ possibilities.detect {|f| File.exist?(f)} || raise(RuntimeError, %(Gemfile not found:\n #{possibilities * "\n"}))
35
+ end
36
+
37
+ def set_gemfile(gemfile)
38
+ gemfile = expand_gemfile(gemfile || "rails3.0")
39
+ ensure_installed(File.dirname(gemfile))
40
+ ENV["BUNDLE_GEMFILE"] = gemfile
41
+ end
42
+ end
@@ -0,0 +1,117 @@
1
+ require 'rubygems'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ require 'forwardable'
5
+ require 'tempfile'
6
+ require 'spec/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 localized_command(command, args)
61
+ case command
62
+ when 'spork'
63
+ command = SporkWorld::BINARY
64
+ when 'cucumber'
65
+ command = Cucumber::BINARY
66
+ else
67
+ command = %x{which #{command}}.chomp
68
+ end
69
+ "#{SporkWorld::RUBY_BINARY} -I #{Cucumber::LIBDIR} #{command} #{args}"
70
+ end
71
+
72
+ def run(command)
73
+ stderr_file = Tempfile.new('spork')
74
+ stderr_file.close
75
+ in_current_dir do
76
+ @last_stdout = `env RUBYOPT= bundle exec #{command} 2> #{stderr_file.path}`
77
+ @last_exit_status = $?.exitstatus
78
+ end
79
+ @last_stderr = IO.read(stderr_file.path)
80
+ end
81
+
82
+ def run_in_background(command)
83
+ in_current_dir do
84
+ @background_job = BackgroundJob.run("env RUBYOPT= bundle exec " + command)
85
+ end
86
+ @background_jobs << @background_job
87
+ @background_job
88
+ end
89
+
90
+ def terminate_background_jobs
91
+ if @background_jobs
92
+ @background_jobs.each do |background_job|
93
+ background_job.kill
94
+ end
95
+ end
96
+ @background_jobs.clear
97
+ @background_job = nil
98
+ end
99
+ end
100
+
101
+ require(SPORK_ROOT + "features/support/bundler_helpers.rb")
102
+ BundlerHelpers.set_gemfile(ENV["GEMFILE"])
103
+
104
+
105
+ World do
106
+ SporkWorld.new
107
+ end
108
+
109
+ Before do
110
+ FileUtils.rm_rf SporkWorld::SANDBOX_DIR
111
+ FileUtils.mkdir_p SporkWorld::SANDBOX_DIR
112
+ end
113
+
114
+ After do
115
+ # FileUtils.rm_rf SporkWorld::SANDBOX_DIR
116
+ terminate_background_jobs
117
+ 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
+
data/lib/spork.rb ADDED
@@ -0,0 +1,156 @@
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 == :using_spork
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
+ # Used by the server. Sets the state to activate spork. Otherwise, prefork and each_run are run in passive mode, allowing specs without a Spork server.
51
+ def using_spork!
52
+ @state = :using_spork
53
+ end
54
+
55
+ def using_spork?
56
+ @state == :using_spork
57
+ end
58
+
59
+ # Used by the server. Returns the current state of Spork.
60
+ def state
61
+ @state ||= :not_using_spork
62
+ end
63
+
64
+ # Used by the server. Called when loading the prefork blocks of the code.
65
+ def exec_prefork(&block)
66
+ using_spork!
67
+ yield
68
+ end
69
+
70
+ # Used by the server. Called to run all of the prefork blocks.
71
+ def exec_each_run(&block)
72
+ activate_after_each_run_at_exit_hook
73
+ each_run_procs.each { |p| p.call }
74
+ each_run_procs.clear
75
+ yield if block_given?
76
+ end
77
+
78
+ # Used by the server. Called to run all of the after_each_run blocks.
79
+ def exec_after_each_run
80
+ # processes in reverse order similar to at_exit
81
+ while p = after_each_run_procs.pop; p.call; end
82
+ true
83
+ end
84
+
85
+ # Traps an instance method of a class (or module) so any calls to it don't actually run until Spork.exec_each_run
86
+ def trap_method(klass, method_name)
87
+ method_name_without_spork, method_name_with_spork = alias_method_names(method_name, :spork)
88
+
89
+ klass.class_eval <<-EOF, __FILE__, __LINE__ + 1
90
+ alias :#{method_name_without_spork} :#{method_name} unless method_defined?(:#{method_name_without_spork})
91
+ def #{method_name}(*args, &block)
92
+ Spork.each_run(false) do
93
+ #{method_name_without_spork}(*args, &block)
94
+ end
95
+ end
96
+ EOF
97
+ end
98
+
99
+ # Same as trap_method, but for class methods instead
100
+ def trap_class_method(klass, method_name)
101
+ trap_method((class << klass; self; end), method_name)
102
+ end
103
+
104
+ def detect_and_require(subfolder)
105
+ ([LIBDIR.to_s] + other_spork_gem_load_paths).uniq.each do |gem_path|
106
+ Dir.glob(File.join(gem_path, subfolder)).each { |file| require file }
107
+ end
108
+ end
109
+
110
+ def other_spork_gem_load_paths
111
+ @other_spork_gem_load_paths ||= (
112
+ Gem.latest_load_paths.grep(/spork/).select do |g|
113
+ not g.match(%r{/spork-[0-9\-.]+/lib}) # don't include other versions of spork
114
+ end
115
+ )
116
+ end
117
+
118
+ private
119
+ def activate_after_each_run_at_exit_hook
120
+ Kernel.module_eval do
121
+ def at_exit(&block)
122
+ Spork.after_each_run(false, &block)
123
+ end
124
+ end
125
+ end
126
+
127
+ def alias_method_names(method_name, feature)
128
+ /^(.+?)([\?\!]{0,1})$/.match(method_name.to_s)
129
+ ["#{$1}_without_spork#{$2}", "#{$1}_with_spork#{$2}"]
130
+ end
131
+
132
+ def already_ran
133
+ @already_ran ||= []
134
+ end
135
+
136
+ def expanded_caller(caller_line)
137
+ file, line = caller_line.split(/:(\d+)/)
138
+ line.gsub(/:.+/, '')
139
+ File.expand_path(file, Dir.pwd) + ":" + line
140
+ end
141
+
142
+ def already_ran?(caller_script_and_line)
143
+ return true if already_ran.include?(expanded_caller(caller_script_and_line))
144
+ already_ran << expanded_caller(caller_script_and_line)
145
+ false
146
+ end
147
+
148
+ def each_run_procs
149
+ @each_run_procs ||= []
150
+ end
151
+
152
+ def after_each_run_procs
153
+ @after_each_run_procs ||= []
154
+ end
155
+ end
156
+ 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