spork 0.9.0.rc8-x86-mingw32

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 +10 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +134 -0
  4. data/assets/bootstrap.rb +47 -0
  5. data/bin/spork +20 -0
  6. data/features/at_exit_during_each_run.feature +36 -0
  7. data/features/cucumber_rails_integration.feature +107 -0
  8. data/features/diagnostic_mode.feature +41 -0
  9. data/features/gemfiles/rails3.0/Gemfile +14 -0
  10. data/features/gemfiles/rails3.0/Gemfile.lock +120 -0
  11. data/features/rails_delayed_loading_workarounds.feature +177 -0
  12. data/features/rspec_rails_integration.feature +92 -0
  13. data/features/spork_debugger.feature +108 -0
  14. data/features/steps/general_steps.rb +3 -0
  15. data/features/steps/rails_steps.rb +67 -0
  16. data/features/steps/sandbox_steps.rb +115 -0
  17. data/features/support/background_job.rb +63 -0
  18. data/features/support/bundler_helpers.rb +41 -0
  19. data/features/support/env.rb +105 -0
  20. data/features/unknown_app_framework.feature +42 -0
  21. data/lib/spork.rb +155 -0
  22. data/lib/spork/app_framework.rb +80 -0
  23. data/lib/spork/app_framework/padrino.rb +22 -0
  24. data/lib/spork/app_framework/rails.rb +82 -0
  25. data/lib/spork/app_framework/unknown.rb +6 -0
  26. data/lib/spork/custom_io_streams.rb +25 -0
  27. data/lib/spork/diagnoser.rb +105 -0
  28. data/lib/spork/ext/rails-reloader.rb +14 -0
  29. data/lib/spork/ext/ruby-debug.rb +150 -0
  30. data/lib/spork/forker.rb +71 -0
  31. data/lib/spork/gem_helpers.rb +38 -0
  32. data/lib/spork/run_strategy.rb +48 -0
  33. data/lib/spork/run_strategy/forking.rb +35 -0
  34. data/lib/spork/run_strategy/magazine.rb +151 -0
  35. data/lib/spork/run_strategy/magazine/magazine_slave.rb +30 -0
  36. data/lib/spork/run_strategy/magazine/magazine_slave_provider.rb +30 -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 +77 -0
  40. data/lib/spork/test_framework.rb +167 -0
  41. data/lib/spork/test_framework/cucumber.rb +38 -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 +173 -0
@@ -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
@@ -0,0 +1,22 @@
1
+ class Spork::AppFramework::Padrino < Spork::AppFramework
2
+
3
+ def preload(&block)
4
+ STDERR.puts "Preloading Padrino environment"
5
+ STDERR.flush
6
+ ENV["PADRINO_ENV"] ||= "test"
7
+ require boot_file
8
+ # Make it so that we don't have to restart Spork if we change, say, a model or routes
9
+ Spork.each_run { ::Padrino.reload! }
10
+ yield
11
+ end
12
+
13
+ def entry_point
14
+ @entry_point ||= File.expand_path("config/boot.rb", Dir.pwd)
15
+ end
16
+ alias :boot_file :entry_point
17
+
18
+ def boot_contents
19
+ @boot_contents ||= File.read(boot_file)
20
+ end
21
+
22
+ end
@@ -0,0 +1,82 @@
1
+ class Spork::AppFramework::Rails < Spork::AppFramework
2
+
3
+ def preload(&block)
4
+ STDERR.puts "Preloading Rails environment"
5
+ STDERR.flush
6
+ ENV["RAILS_ENV"] ||= 'test'
7
+ preload_rails
8
+ yield
9
+ end
10
+
11
+ def entry_point
12
+ @entry_point ||= File.expand_path("config/environment.rb", Dir.pwd)
13
+ end
14
+
15
+ alias :environment_file :entry_point
16
+
17
+ def boot_file
18
+ @boot_file ||= File.join(File.dirname(environment_file), 'boot')
19
+ end
20
+
21
+ def application_file
22
+ @application_file ||= File.join(File.dirname(environment_file), 'application')
23
+ end
24
+
25
+ def environment_contents
26
+ @environment_contents ||= File.read(environment_file)
27
+ end
28
+
29
+ def vendor
30
+ @vendor ||= File.expand_path("vendor/rails", Dir.pwd)
31
+ end
32
+
33
+ def deprecated_version
34
+ @version ||= (
35
+ if /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/.match(environment_contents)
36
+ $1
37
+ else
38
+ nil
39
+ end
40
+ )
41
+ end
42
+
43
+ def preload_rails
44
+ if deprecated_version && (not /^3/.match(deprecated_version))
45
+ puts "This version of spork only supports Rails 3.0. To use spork with rails 2.3.x, downgrade to spork 0.8.x."
46
+ exit 1
47
+ end
48
+ require application_file
49
+ ::Rails.application
50
+ ::Rails::Engine.class_eval do
51
+ def eager_load!
52
+ # turn off eager_loading, all together
53
+ end
54
+ end
55
+ # Spork.trap_method(::AbstractController::Helpers::ClassMethods, :helper)
56
+ Spork.trap_method(::ActiveModel::Observing::ClassMethods, :instantiate_observers)
57
+ Spork.each_run { ActiveRecord::Base.establish_connection rescue nil } if Object.const_defined?(:ActiveRecord)
58
+
59
+
60
+ AbstractController::Helpers::ClassMethods.module_eval do
61
+ def helper(*args, &block)
62
+ ([args].flatten - [:all]).each do |arg|
63
+ next unless arg.is_a?(String)
64
+ filename = arg + "_helper"
65
+ unless ::ActiveSupport::Dependencies.search_for_file(filename)
66
+ # this error message must raise in the format such that LoadError#path returns the filename
67
+ raise LoadError.new("Missing helper file helpers/%s.rb" % filename)
68
+ end
69
+ end
70
+
71
+ Spork.each_run(false) do
72
+ modules_for_helpers(args).each do |mod|
73
+ add_template_helper(mod)
74
+ end
75
+
76
+ _helpers.module_eval(&block) if block_given?
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,6 @@
1
+ # This is used if no supported appliction framework is detected
2
+ class Spork::AppFramework::Unknown < Spork::AppFramework
3
+ def entry_point
4
+ nil
5
+ end
6
+ end
@@ -0,0 +1,25 @@
1
+ # This class is mainly used for testing.
2
+ # When included (and used), it gives us an opportunity to stub out the output streams used for a given class
3
+ module Spork::CustomIOStreams
4
+ def self.included(klass)
5
+ klass.send(:extend, ::Spork::CustomIOStreams::ClassMethods)
6
+ end
7
+
8
+ def stderr
9
+ self.class.stderr
10
+ end
11
+
12
+ def stdout
13
+ self.class.stdout
14
+ end
15
+
16
+ module ClassMethods
17
+ def stderr
18
+ $stderr
19
+ end
20
+
21
+ def stdout
22
+ $stdout
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,105 @@
1
+ # The Diagnoser hooks into load and require and keeps track of when files are required / loaded, and who loaded them.
2
+ # It's used when you run spork --diagnose
3
+ #
4
+ # = Example
5
+ #
6
+ # Spork::Diagnoser.install_hook!('/path/env.rb', '/path')
7
+ # require '/path/to/env.rb'
8
+ # Spork::Diagnoser.output_results(STDOUT)
9
+ class Spork::Diagnoser
10
+ class << self
11
+ def loaded_files
12
+ @loaded_files ||= {}
13
+ end
14
+
15
+ # Installs the diagnoser hook into Kernel#require and Kernel#load
16
+ #
17
+ # == Parameters
18
+ #
19
+ # * +entry_file+ - The file that is used to load the project. Used to filter the backtrace so anything that happens after it is hidden.
20
+ # * +dir+ - The project directory. Any file loaded outside of this directory will not be logged.
21
+ def install_hook!(entry_file = nil, dir = Dir.pwd)
22
+ @dir = File.expand_path(Dir.pwd, dir)
23
+ @entry_file = entry_file
24
+
25
+ Kernel.class_eval do
26
+ alias :require_without_diagnoser :require
27
+ alias :load_without_diagnoser :load
28
+
29
+ def require(string)
30
+ ::Spork::Diagnoser.add_included_file(string, caller)
31
+ require_without_diagnoser(string)
32
+ end
33
+ private :require
34
+
35
+ def load(string)
36
+ ::Spork::Diagnoser.add_included_file(string, caller)
37
+ load_without_diagnoser(string)
38
+ end
39
+ private :load
40
+ end
41
+ end
42
+
43
+ def add_included_file(filename, callstack)
44
+ filename = expand_filename(filename)
45
+ return unless File.exist?(filename)
46
+ loaded_files[filename] = filter_callstack(caller) if subdirectory?(filename)
47
+ end
48
+
49
+ # Uninstall the hook. Generally useful only for testing the Diagnoser.
50
+ def remove_hook!
51
+ return unless Kernel.private_instance_methods.map(&:to_sym).include?(:require_without_diagnoser)
52
+ Kernel.class_eval do
53
+ alias :require :require_without_diagnoser
54
+ alias :load :load_without_diagnoser
55
+
56
+ undef_method(:require_without_diagnoser)
57
+ undef_method(:load_without_diagnoser)
58
+ end
59
+ true
60
+ end
61
+
62
+ # output the results of a diagnostic run.
63
+ #
64
+ # == Parameters
65
+ #
66
+ # * +stdout+ - An IO stream to output the results to.
67
+ def output_results(stdout)
68
+ project_prefix = Dir.pwd + "/"
69
+ minimify = lambda { |f| f.gsub(project_prefix, '')}
70
+ stdout.puts "- Spork Diagnosis -\n"
71
+ stdout.puts "-- Summary --"
72
+ stdout.puts loaded_files.keys.sort.map(&minimify)
73
+ stdout.puts "\n\n\n"
74
+ stdout.puts "-- Detail --"
75
+ loaded_files.keys.sort.each do |file|
76
+ stdout.puts "\n\n\n--- #{minimify.call(file)} ---\n"
77
+ stdout.puts loaded_files[file].map(&minimify)
78
+ end
79
+ end
80
+
81
+ private
82
+ def filter_callstack(callstack, entry_file = @entry_file)
83
+ callstack.pop until callstack.empty? || callstack.last.include?(@entry_file) if @entry_file
84
+ callstack.map do |line|
85
+ next if line.include?('lib/spork/diagnoser.rb')
86
+ line.gsub!('require_without_diagnoser', 'require')
87
+ line
88
+ end.compact
89
+ end
90
+
91
+ def expand_filename(filename)
92
+ ([Dir.pwd] + $:).each do |attempted_path|
93
+ attempted_filename = File.expand_path(filename, attempted_path)
94
+ return attempted_filename if File.file?(attempted_filename)
95
+ attempted_filename = attempted_filename + ".rb"
96
+ return attempted_filename if File.file?(attempted_filename)
97
+ end
98
+ filename
99
+ end
100
+
101
+ def subdirectory?(directory)
102
+ File.expand_path(directory, Dir.pwd).include?(@dir)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,14 @@
1
+ Spork.each_run do
2
+ ::ActiveSupport.const_defined?(:Dependencies) ?
3
+ ::ActiveSupport::Dependencies.mechanism = :load :
4
+ ::Dependencies.mechanism = :load
5
+
6
+ require 'action_controller/dispatcher'
7
+ dispatcher = ::ActionController::Dispatcher.new($stdout)
8
+
9
+ if ::ActionController::Dispatcher.respond_to?(:reload_application)
10
+ ::ActionController::Dispatcher.reload_application
11
+ else
12
+ dispatcher.reload_application
13
+ end
14
+ end if Spork.using_spork?
@@ -0,0 +1,150 @@
1
+ require 'socket'
2
+ require 'forwardable'
3
+
4
+ begin
5
+ require 'ruby-debug'
6
+
7
+ # Experimental!
8
+
9
+ class SporkDebugger
10
+ DEFAULT_PORT = 10_123
11
+ HOST = '127.0.0.1'
12
+
13
+ extend Forwardable
14
+ def_delegators :state, *[:prepare_debugger, :initialize]
15
+ attr_reader :state
16
+
17
+ class << self
18
+ attr_reader :instance
19
+ def run
20
+ @instance ||= new
21
+ end
22
+ end
23
+
24
+ def initialize
25
+ @state = SporkDebugger::PreloadState.new
26
+ Spork.send(:each_run_procs).unshift(lambda do
27
+ @state = @state.transition_to_each_run_state
28
+ end)
29
+ end
30
+
31
+ module NetworkHelpers
32
+ def find_port(starting_with)
33
+ port = starting_with
34
+ begin
35
+ server = TCPServer.new(HOST, port)
36
+ server.close
37
+ rescue Errno::EADDRINUSE
38
+ port += 1
39
+ retry
40
+ end
41
+
42
+ port
43
+ end
44
+ end
45
+
46
+ class PreloadState
47
+ include NetworkHelpers
48
+ def initialize
49
+ Spork.each_run { install_hook }
50
+ listen_for_connection_signals
51
+ end
52
+
53
+ def finish
54
+ @tcp_service.close; @tcp_service = nil;
55
+ end
56
+
57
+ def transition_to_each_run_state
58
+ finish
59
+ SporkDebugger::EachRunState.new(@port)
60
+ end
61
+
62
+ protected
63
+ def install_hook
64
+ Kernel.class_eval do
65
+ alias :debugger_without_spork_hook :debugger
66
+ def debugger(steps = 1)
67
+ SporkDebugger.instance.prepare_debugger
68
+ debugger_without_spork_hook
69
+ end
70
+ end
71
+ end
72
+
73
+ def listen_for_connection_signals
74
+ @port = SporkDebugger::DEFAULT_PORT
75
+ begin
76
+ @tcp_service = TCPServer.new(SporkDebugger::HOST, @port)
77
+ rescue Errno::EADDRINUSE
78
+ @port += 1
79
+ retry
80
+ end
81
+ Thread.new { main_loop }
82
+ end
83
+
84
+ def main_loop
85
+ while @tcp_service do
86
+ socket = @tcp_service.accept
87
+ port = Marshal.load(socket)
88
+ Marshal.dump(true, socket)
89
+ connect_rdebug_client(port)
90
+ socket.close
91
+ end
92
+ rescue => e
93
+ puts "error: #{e.class} - #{e}"
94
+ end
95
+
96
+ def connect_rdebug_client(port = Debugger::PORT)
97
+ puts "\n\n - Debug Session Started - \n\n\n"
98
+ begin
99
+ Debugger.start_client(SporkDebugger::HOST, port)
100
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED
101
+ end
102
+ puts "\n\n - Debug Session Terminated - \n\n\n"
103
+ end
104
+ end
105
+
106
+ class EachRunState
107
+ include NetworkHelpers
108
+ def initialize(connection_request_port)
109
+ @connection_request_port = connection_request_port
110
+ end
111
+
112
+ def prepare_debugger
113
+ return if @debugger_prepared
114
+ @debugger_prepared = true
115
+ port, cport = start_rdebug_server
116
+ signal_spork_server_to_connect_to_rdebug_server(port)
117
+ wait_for_connection
118
+ puts "\n\n - breakpoint - see your spork server for the debug terminal - \n\n"
119
+ end
120
+
121
+ def signal_spork_server_to_connect_to_rdebug_server(rdebug_server_port)
122
+ socket = TCPSocket.new(SporkDebugger::HOST, @connection_request_port)
123
+ Marshal.dump(rdebug_server_port, socket)
124
+ response = Marshal.load(socket)
125
+ socket.close
126
+ end
127
+
128
+ def start_rdebug_server
129
+ Debugger.stop if Debugger.started?
130
+ port = find_port(Debugger::PORT)
131
+ cport = find_port(port + 1)
132
+ Debugger.start_remote(SporkDebugger::HOST, [port, cport]) do
133
+ Debugger.run_init_script(StringIO.new)
134
+ end
135
+ Debugger.start
136
+ [port, cport]
137
+ end
138
+
139
+ protected
140
+ def wait_for_connection
141
+ while Debugger.handler.interface.nil?; sleep 0.10; end
142
+ end
143
+ end
144
+ end
145
+
146
+ Spork.prefork { SporkDebugger.run } if Spork.using_spork?
147
+
148
+ rescue LoadError
149
+ raise LoadError, "Your project has loaded spork/ext/ruby-debug, which relies on the ruby-debug gem. It appears that ruby-debug is not installed. Please install it."
150
+ end