timcharper-spork 0.4.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ Feature: Diagnostic Mode
2
+ To help a developer quickly pinpoint why files are being loaded
3
+ Spork provides a diagnostic mode
4
+ That provides a list of which project files were loaded during prefork, and who loaded them.
5
+
6
+ Scenario: Running spork --diagnose
7
+ Given I am in the directory "test_project"
8
+ And a file named "spec/spec_helper.rb" with:
9
+ """
10
+ require 'rubygems'
11
+ require 'spork'
12
+
13
+ Spork.prefork do
14
+ require 'lib/awesome.rb'
15
+ require '../external_dependency/super_duper.rb'
16
+ end
17
+
18
+ Spork.each_run do
19
+ puts "I'm loading the stuff just for this run..."
20
+ end
21
+ """
22
+ And a file named "lib/awesome.rb" with:
23
+ """
24
+ class Awesome
25
+ end
26
+ """
27
+ And a file named "../external_dependency/super_duper.rb" with:
28
+ """
29
+ class Awesome
30
+ end
31
+ """
32
+ When I run spork --diagnose
33
+ Then the output should contain "lib/awesome.rb"
34
+ And the output should contain "spec/spec_helper.rb:5"
35
+ And the output should not contain "super_duper.rb"
36
+ And the output should not contain "diagnose.rb"
37
+
@@ -0,0 +1,77 @@
1
+ Feature: Rails Integration
2
+ To get a developer up and running quickly
3
+ Spork automatically integrates with rails
4
+ Providing default hooks and behaviors
5
+
6
+ Background: Rails App with RSpec and Spork
7
+
8
+ Given I am in a fresh rails project named "test_rails_project"
9
+ And a file named "spec/spec_helper.rb" with:
10
+ """
11
+ require 'rubygems'
12
+ require 'spork'
13
+ require 'spec'
14
+
15
+ Spork.prefork do
16
+ $run_phase = :prefork
17
+ require File.dirname(__FILE__) + '/../config/environment.rb'
18
+ end
19
+
20
+ Spork.each_run do
21
+ $run_phase = :each_run
22
+ puts "I'm loading the stuff just for this run..."
23
+ end
24
+
25
+ class ActiveRecord::Base
26
+ class << self
27
+ def establish_connection
28
+ ($loaded_stuff ||= []) << 'establish_connection'
29
+ puts "Database connection was automatically re-established!"
30
+ end
31
+ end
32
+ end
33
+ """
34
+ And a file named "app/models/user.rb" with:
35
+ """
36
+ class User < ActiveRecord::Base
37
+ ($loaded_stuff ||= []) << 'User'
38
+ end
39
+ """
40
+ And a file named "app/helpers/application_helper.rb" with:
41
+ """
42
+ module ApplicationHelper
43
+ ($loaded_stuff ||= []) << 'ApplicationHelper'
44
+ end
45
+ """
46
+ And a file named "app/models/user_observer.rb" with:
47
+ """
48
+ class UserObserver < ActiveRecord::Observer
49
+ ($loaded_stuff ||= []) << 'UserObserver'
50
+ end
51
+ """
52
+ And a file named "spec/models/user_spec.rb" with:
53
+ """
54
+ describe User do
55
+ it "does absoluately nothing" do
56
+ Spork.state.should == :using_spork
57
+ $loaded_stuff.should include('establish_connection')
58
+ $loaded_stuff.should include('User')
59
+ $loaded_stuff.should include('UserObserver')
60
+ $loaded_stuff.should include('ApplicationHelper')
61
+ puts "Specs successfully run within spork, and all initialization files were loaded"
62
+ end
63
+ end
64
+ """
65
+ Scenario: Analyzing files were preloaded
66
+ When I run spork --diagnose
67
+ Then the output should not contain "user_observer.rb"
68
+ Then the output should not contain "user.rb"
69
+ Then the output should not contain "app/controllers/application.rb"
70
+ Then the output should not contain "app/controllers/application_controller.rb"
71
+ Then the output should not contain "app/controllers/application_helper.rb"
72
+
73
+ Scenario: Running spork with a rails app and observers
74
+
75
+ When I fire up a spork instance with "spork rspec"
76
+ And I run spec --drb spec/models/user_spec.rb
77
+ Then the output should contain "Specs successfully run within spork, and all initialization files were loaded"
@@ -0,0 +1,5 @@
1
+ Given /^I am in a fresh rails project named "(.+)"$/ do |folder_name|
2
+ @current_dir = SporkWorld::SANDBOX_DIR
3
+ run("#{SporkWorld::RUBY_BINARY} #{%x{which rails}.chomp} #{folder_name}")
4
+ @current_dir = File.join(File.join(SporkWorld::SANDBOX_DIR, folder_name))
5
+ end
@@ -0,0 +1,60 @@
1
+ Given /^I am in the directory "(.*)"$/ do |sandbox_dir_relative_path|
2
+ path = File.join(SporkWorld::SANDBOX_DIR, sandbox_dir_relative_path)
3
+ FileUtils.mkdir_p(path)
4
+ @current_dir = File.join(path)
5
+ end
6
+
7
+ Given /^a file named "([^\"]*)"$/ do |file_name|
8
+ create_file(file_name, '')
9
+ end
10
+
11
+ Given /^a file named "([^\"]*)" with:$/ do |file_name, file_content|
12
+ create_file(file_name, file_content)
13
+ end
14
+
15
+ When /^I run (spork|spec)($| .*$)/ do |command, spork_opts|
16
+ if command == 'spork'
17
+ command = SporkWorld::BINARY
18
+ else
19
+ command = %x{which #{command}}.chomp
20
+ end
21
+ run "#{SporkWorld::RUBY_BINARY} #{command} #{spork_opts}"
22
+ end
23
+
24
+ When /^I fire up a spork instance with "spork(.*)"$/ do |spork_opts|
25
+ run_in_background "#{SporkWorld::RUBY_BINARY} #{SporkWorld::BINARY} #{spork_opts}"
26
+ output = ""
27
+ begin
28
+ status = Timeout::timeout(15) do
29
+ # Something that should be interrupted if it takes too much time...
30
+ while line = @bg_stderr.gets
31
+ output << line
32
+ puts line
33
+ break if line.include?("Spork is ready and listening")
34
+ end
35
+ end
36
+ rescue Timeout::Error
37
+ puts "I can't seem to launch Spork properly. Output was:\n#{output}"
38
+ true.should == false
39
+ end
40
+ end
41
+
42
+ Then /^the output should contain$/ do |text|
43
+ last_stdout.should include(text)
44
+ end
45
+
46
+ Then /^the output should contain "(.+)"$/ do |text|
47
+ last_stdout.should include(text)
48
+ end
49
+
50
+ Then /^the output should not contain$/ do |text|
51
+ last_stdout.should_not include(text)
52
+ end
53
+
54
+ Then /^the output should not contain "(.+)"$/ do |text|
55
+ last_stdout.should_not include(text)
56
+ end
57
+
58
+ Then /^the output should be$/ do |text|
59
+ last_stdout.should == text
60
+ end
@@ -0,0 +1,98 @@
1
+ require 'rubygems'
2
+ require 'fileutils'
3
+ require 'forwardable'
4
+ require 'tempfile'
5
+ require 'spec/expectations'
6
+ require 'timeout'
7
+
8
+ class SporkWorld
9
+ RUBY_BINARY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
10
+ BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/spork')
11
+ SANDBOX_DIR = File.expand_path(File.join(File.dirname(__FILE__), '../../tmp/sandbox'))
12
+
13
+ extend Forwardable
14
+ def_delegators SporkWorld, :sandbox_dir, :spork_lib_dir
15
+
16
+ def spork_lib_dir
17
+ @spork_lib_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
18
+ end
19
+
20
+ def initialize
21
+ @current_dir = SANDBOX_DIR
22
+ @background_jobs = []
23
+ end
24
+
25
+ private
26
+ attr_reader :last_exit_status, :last_stderr, :last_stdout, :background_jobs
27
+
28
+ def create_file(file_name, file_content)
29
+ file_content.gsub!("SPORK_LIB", "'#{spork_lib_dir}'") # Some files, such as Rakefiles need to use the lib dir
30
+ in_current_dir do
31
+ FileUtils.mkdir_p(File.dirname(file_name))
32
+ File.open(file_name, 'w') { |f| f << file_content }
33
+ end
34
+ end
35
+
36
+ def in_current_dir(&block)
37
+ Dir.chdir(@current_dir, &block)
38
+ end
39
+
40
+ def run(command)
41
+ stderr_file = Tempfile.new('spork')
42
+ stderr_file.close
43
+ in_current_dir do
44
+ @last_stdout = `#{command} 2> #{stderr_file.path}`
45
+ @last_exit_status = $?.exitstatus
46
+ end
47
+ @last_stderr = IO.read(stderr_file.path)
48
+ end
49
+
50
+ def run_in_background(command)
51
+ child_stdin, parent_stdin = IO::pipe
52
+ parent_stdout, child_stdout = IO::pipe
53
+ parent_stderr, child_stderr = IO::pipe
54
+
55
+ background_jobs << Kernel.fork do
56
+ # grandchild
57
+ [parent_stdin, parent_stdout, parent_stderr].each { |io| io.close }
58
+
59
+ STDIN.reopen(child_stdin)
60
+ STDOUT.reopen(child_stdout)
61
+ STDERR.reopen(child_stderr)
62
+
63
+ [child_stdin, child_stdout, child_stderr].each { |io| io.close }
64
+
65
+ in_current_dir do
66
+ exec command
67
+ end
68
+ end
69
+
70
+ [child_stdin, child_stdout, child_stderr].each { |io| io.close }
71
+ parent_stdin.sync = true
72
+
73
+ @bg_stdin, @bg_stdout, @bg_stderr = [parent_stdin, parent_stdout, parent_stderr]
74
+ end
75
+
76
+ def terminate_background_jobs
77
+ if @background_jobs
78
+ @background_jobs.each do |pid|
79
+ Process.kill(Signal.list['TERM'], pid)
80
+ end
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ World do
87
+ SporkWorld.new
88
+ end
89
+
90
+ Before do
91
+ FileUtils.rm_rf SporkWorld::SANDBOX_DIR
92
+ FileUtils.mkdir_p SporkWorld::SANDBOX_DIR
93
+ end
94
+
95
+ After do
96
+ # FileUtils.rm_rf SporkWorld::SANDBOX_DIR
97
+ terminate_background_jobs
98
+ end
@@ -0,0 +1,113 @@
1
+ class Spork::AppFramework::Rails < Spork::AppFramework
2
+
3
+ # TODO - subclass this out to handle different versions of rails
4
+ class NinjaPatcher
5
+ def self.run
6
+ install_hook
7
+ end
8
+
9
+ def self.install_hook
10
+ ::Rails::Initializer.class_eval do
11
+ alias :require_frameworks_without_spork :require_frameworks unless method_defined?(:require_frameworks_without_spork)
12
+ def require_frameworks
13
+ result = require_frameworks_without_spork
14
+ Spork::AppFramework[:Rails].ninja_patcher.install_specific_hooks
15
+ result
16
+ end
17
+ end
18
+ end
19
+
20
+ def self.install_specific_hooks
21
+ auto_reestablish_db_connection
22
+ delay_observer_loading
23
+ delay_app_preload
24
+ delay_application_controller_loading
25
+ end
26
+
27
+ def self.delay_observer_loading
28
+ if Object.const_defined?(:ActiveRecord)
29
+ Spork.trap_method(::ActiveRecord::Observing::ClassMethods, :instantiate_observers)
30
+ end
31
+ if Object.const_defined?(:ActionController)
32
+ require "action_controller/dispatcher.rb"
33
+ Spork.trap_class_method(::ActionController::Dispatcher, :define_dispatcher_callbacks)
34
+ end
35
+ end
36
+
37
+ def self.delay_app_preload
38
+ if ::Rails::Initializer.instance_methods.include?('load_application_classes')
39
+ Spork.trap_method(::Rails::Initializer, :load_application_classes)
40
+ end
41
+ end
42
+
43
+ def self.delay_application_controller_loading
44
+ if application_controller_source = ["#{Dir.pwd}/app/controllers/application.rb", "#{Dir.pwd}/app/controllers/application_controller.rb"].find { |f| File.exist?(f) }
45
+ application_helper_source = "#{Dir.pwd}/app/helpers/application_helper.rb"
46
+ load_paths = (Object.const_defined?(:Dependencies) ? ::Dependencies : ::ActiveSupport::Dependencies).load_paths
47
+ load_paths.unshift(File.expand_path('rails_stub_files', File.dirname(__FILE__)))
48
+ Spork.each_run do
49
+ require application_controller_source
50
+ require application_helper_source if File.exist?(application_helper_source)
51
+ end
52
+ end
53
+ end
54
+
55
+ def self.auto_reestablish_db_connection
56
+ if Object.const_defined?(:ActiveRecord)
57
+ Spork.each_run do
58
+ ActiveRecord::Base.establish_connection
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def bootstrap_required?
65
+ false
66
+ end
67
+
68
+ def preload(&block)
69
+ STDERR.puts "Preloading Rails environment"
70
+ STDERR.flush
71
+ ENV["RAILS_ENV"] ||= 'test'
72
+ preload_rails
73
+ require environment_file
74
+ yield
75
+ end
76
+
77
+ def environment_file
78
+ @environment_file ||= File.expand_path("config/environment.rb", Dir.pwd)
79
+ end
80
+
81
+ def boot_file
82
+ @boot_file ||= File.join(File.dirname(environment_file), 'boot')
83
+ end
84
+
85
+ def environment_contents
86
+ @environment_contents ||= File.read(environment_file)
87
+ end
88
+
89
+ def vendor
90
+ @vendor ||= File.expand_path("vendor/rails", Dir.pwd)
91
+ end
92
+
93
+ def version
94
+ @version ||= (
95
+ if /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/.match(environment_contents)
96
+ $1
97
+ else
98
+ nil
99
+ end
100
+ )
101
+ end
102
+
103
+ def ninja_patcher
104
+ ::Spork::AppFramework::Rails::NinjaPatcher
105
+ end
106
+
107
+ def preload_rails
108
+ Object.const_set(:RAILS_GEM_VERSION, version) if version
109
+ require boot_file
110
+ ninja_patcher.run
111
+ end
112
+
113
+ end
@@ -0,0 +1,2 @@
1
+ class ::ApplicationController < ActionController::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ class ::ApplicationController < ActionController::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ module ::ApplicationHelper
2
+ end
@@ -0,0 +1,6 @@
1
+ class Spork::AppFramework::Unknown < Spork::AppFramework
2
+ def bootstrap_required?
3
+ true
4
+ end
5
+
6
+ end
@@ -0,0 +1,46 @@
1
+ class Spork::AppFramework
2
+ SUPPORTED_FRAMEWORKS = {
3
+ :Rails => lambda do
4
+ File.exist?("config/environment.rb") && File.read("config/environment.rb").include?('RAILS_GEM_VERSION')
5
+ end
6
+ }
7
+
8
+ def self.detect_framework_name
9
+ SUPPORTED_FRAMEWORKS.each do |key, value|
10
+ return key if value.call
11
+ end
12
+ :Unknown
13
+ end
14
+
15
+ def self.detect_framework
16
+ name = detect_framework_name
17
+ self[name]
18
+ end
19
+
20
+ def self.[](name)
21
+ instances[name] ||= (
22
+ require File.join(File.dirname(__FILE__), "app_framework", name.to_s.downcase)
23
+ const_get(name).new
24
+ )
25
+ end
26
+
27
+ def self.instances
28
+ @instances ||= {}
29
+ end
30
+
31
+ def self.short_name
32
+ name.gsub('Spork::AppFramework::', '')
33
+ end
34
+
35
+ def bootstrap_required?
36
+ raise NotImplemented
37
+ end
38
+
39
+ def preload(&block)
40
+ yield
41
+ end
42
+
43
+ def name
44
+ self.class.short_name
45
+ end
46
+ end
@@ -0,0 +1,23 @@
1
+ module Spork::CustomIOStreams
2
+ def self.included(klass)
3
+ klass.send(:extend, ::Spork::CustomIOStreams::ClassMethods)
4
+ end
5
+
6
+ def stderr
7
+ self.class.stderr
8
+ end
9
+
10
+ def stdout
11
+ self.class.stdout
12
+ end
13
+
14
+ module ClassMethods
15
+ def stderr
16
+ $stderr
17
+ end
18
+
19
+ def stdout
20
+ $stdout
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,72 @@
1
+ class Spork::Diagnoser
2
+ class << self
3
+ def loaded_files
4
+ @loaded_files ||= {}
5
+ end
6
+
7
+ def install_hook!(dir = Dir.pwd)
8
+ @dir = File.expand_path(Dir.pwd, dir)
9
+
10
+ Kernel.class_eval do
11
+ alias :require_without_diagnoser :require
12
+ alias :load_without_diagnoser :load
13
+
14
+ def require(string)
15
+ ::Spork::Diagnoser.add_included_file(string, caller)
16
+ require_without_diagnoser(string)
17
+ end
18
+
19
+ def load(string)
20
+ ::Spork::Diagnoser.add_included_file(string, caller)
21
+ load_without_diagnoser(string)
22
+ end
23
+ end
24
+ end
25
+
26
+ def add_included_file(filename, callstack)
27
+ filename = expand_filename(filename)
28
+ return unless File.exist?(filename)
29
+ loaded_files[filename] = caller.select { |f| ! f.include?('lib/spork/diagnoser.rb')} if subdirectory?(filename)
30
+ end
31
+
32
+ def expand_filename(filename)
33
+ ([Dir.pwd] + $:).each do |attempted_path|
34
+ attempted_filename = File.expand_path(filename, attempted_path)
35
+ return attempted_filename if File.file?(attempted_filename)
36
+ attempted_filename = attempted_filename + ".rb"
37
+ return attempted_filename if File.file?(attempted_filename)
38
+ end
39
+ filename
40
+ end
41
+
42
+ def subdirectory?(directory)
43
+ File.expand_path(directory, Dir.pwd).include?(@dir)
44
+ end
45
+
46
+ def remove_hook!
47
+ return unless Kernel.private_instance_methods.include?('require_without_diagnoser')
48
+ Kernel.class_eval do
49
+ alias :require :require_without_diagnoser
50
+ alias :load :load_without_diagnoser
51
+
52
+ undef_method(:require_without_diagnoser)
53
+ undef_method(:load_without_diagnoser)
54
+ end
55
+ true
56
+ end
57
+
58
+ def output_results(stdout)
59
+ project_prefix = Dir.pwd + "/"
60
+ minimify = lambda { |f| f.gsub(project_prefix, '')}
61
+ stdout.puts "- Spork Diagnosis -\n"
62
+ stdout.puts "-- Summary --"
63
+ stdout.puts loaded_files.keys.sort.map(&minimify)
64
+ stdout.puts "\n\n\n"
65
+ stdout.puts "-- Detail --"
66
+ loaded_files.keys.sort.each do |file|
67
+ stdout.puts "\n\n\n--- #{minimify.call(file)} ---\n"
68
+ stdout.puts loaded_files[file].map(&minimify)
69
+ end
70
+ end
71
+ end
72
+ end
data/lib/spork/runner.rb CHANGED
@@ -19,6 +19,7 @@ module Spork
19
19
 
20
20
  opt.separator "Options:"
21
21
  opt.on("-b", "--bootstrap") {|ignore| @options[:bootstrap] = true }
22
+ opt.on("-d", "--diagnose") {|ignore| @options[:diagnose] = true }
22
23
  opt.on("-h", "--help") {|ignore| @options[:help] = true }
23
24
  non_option_args = args.select { |arg| ! args[0].match(/^-/) }
24
25
  @options[:server_matcher] = non_option_args[0]
@@ -45,7 +46,7 @@ module Spork
45
46
  if options[:server_matcher]
46
47
  @server = Spork::Server.supported_servers(options[:server_matcher]).first
47
48
  unless @server
48
- @output.puts <<-ERROR
49
+ @error.puts <<-ERROR
49
50
  #{options[:server_matcher].inspect} didn't match a supported test framework.
50
51
 
51
52
  #{supported_servers_text}
@@ -54,7 +55,7 @@ module Spork
54
55
  end
55
56
 
56
57
  unless @server.available?
57
- @output.puts <<-USEFUL_ERROR
58
+ @error.puts <<-USEFUL_ERROR
58
59
  I can't find the helper file #{@server.helper_file} for the #{@server.server_name} testing framework.
59
60
  Are you running me from the project directory?
60
61
  USEFUL_ERROR
@@ -63,7 +64,7 @@ Are you running me from the project directory?
63
64
  else
64
65
  @server = Spork::Server.available_servers.first
65
66
  if @server.nil?
66
- @output.puts <<-USEFUL_ERROR
67
+ @error.puts <<-USEFUL_ERROR
67
68
  I can't find any testing frameworks to use.
68
69
  Are you running me from a project directory?
69
70
  USEFUL_ERROR
@@ -76,12 +77,26 @@ Are you running me from a project directory?
76
77
  def run
77
78
  return false unless find_server
78
79
  ENV["DRB"] = 'true'
79
- ENV["RAILS_ENV"] ||= 'test' if server.using_rails?
80
- @output.puts "Using #{server.server_name}"
81
- return server.bootstrap if options[:bootstrap]
82
- return(false) unless server.preload
83
- server.run
84
- return true
80
+ @error.puts "Using #{server.server_name}"
81
+ @error.flush
82
+ case
83
+ when options[:bootstrap]
84
+ server.bootstrap
85
+ when options[:diagnose]
86
+ require 'spork/diagnoser'
87
+
88
+ Spork::Diagnoser.install_hook!
89
+ server.preload
90
+ Spork::Diagnoser.output_results(@output)
91
+ return true
92
+ else
93
+ return(false) unless server.preload
94
+ server.run
95
+ return true
96
+ end
97
+ end
98
+
99
+ def diagnose
85
100
  end
86
101
 
87
102
  private
data/lib/spork/server.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'drb/drb'
2
2
  require 'rbconfig'
3
3
  require 'spork/forker.rb'
4
+ require 'spork/custom_io_streams.rb'
5
+ require 'spork/app_framework.rb'
4
6
 
5
7
  # This is based off of spec_server.rb from rspec-rails (David Chelimsky), which was based on Florian Weber's TDDMate
6
8
  class Spork::Server
@@ -9,6 +11,8 @@ class Spork::Server
9
11
  LOAD_PREFERENCE = ['RSpec', 'Cucumber']
10
12
  BOOTSTRAP_FILE = File.dirname(__FILE__) + "/../../assets/bootstrap.rb"
11
13
 
14
+ include Spork::CustomIOStreams
15
+
12
16
  def self.port
13
17
  raise NotImplemented
14
18
  end
@@ -45,20 +49,16 @@ class Spork::Server
45
49
  LOAD_PREFERENCE.index(server_name) || LOAD_PREFERENCE.length
46
50
  end
47
51
 
48
- def self.using_rails?
49
- File.exist?("config/environment.rb")
50
- end
51
-
52
52
  def self.bootstrapped?
53
53
  File.read(helper_file).include?("Spork.prefork")
54
54
  end
55
55
 
56
56
  def self.bootstrap
57
57
  if bootstrapped?
58
- puts "Already bootstrapped!"
58
+ stderr.puts "Already bootstrapped!"
59
59
  return
60
60
  end
61
- puts "Bootstrapping #{helper_file}."
61
+ stderr.puts "Bootstrapping #{helper_file}."
62
62
  contents = File.read(helper_file)
63
63
  bootstrap_code = File.read(BOOTSTRAP_FILE)
64
64
  File.open(helper_file, "wb") do |f|
@@ -66,7 +66,7 @@ class Spork::Server
66
66
  f.puts contents
67
67
  end
68
68
 
69
- puts "Done. Edit #{helper_file} now with your favorite text editor and follow the instructions."
69
+ stderr.puts "Done. Edit #{helper_file} now with your favorite text editor and follow the instructions."
70
70
  true
71
71
  end
72
72
 
@@ -80,7 +80,8 @@ class Spork::Server
80
80
  trap("SIGTERM") { abort; exit!(0) }
81
81
  trap("USR2") { abort; restart } if Signal.list.has_key?("USR2")
82
82
  DRb.start_service("druby://127.0.0.1:#{port}", self)
83
- puts "Spork is ready and listening on #{port}!"
83
+ stderr.puts "Spork is ready and listening on #{port}!"
84
+ stderr.flush
84
85
  DRb.thread.join
85
86
  end
86
87
 
@@ -94,9 +95,10 @@ class Spork::Server
94
95
 
95
96
  def run(argv, stderr, stdout)
96
97
  return false if running?
98
+
97
99
  @child = ::Spork::Forker.new do
98
100
  $stdout, $stderr = stdout, stderr
99
- Spork.exec_each_run(helper_file)
101
+ Spork.exec_each_run { load helper_file }
100
102
  run_tests(argv, stderr, stdout)
101
103
  end
102
104
  @child.result
@@ -107,20 +109,30 @@ class Spork::Server
107
109
  end
108
110
 
109
111
  private
112
+ def self.framework
113
+ @framework ||= Spork::AppFramework.detect_framework
114
+ end
115
+
110
116
  def self.preload
111
- if bootstrapped?
112
- puts "Loading Spork.prefork block..."
113
- Spork.exec_prefork(helper_file)
114
- else
115
- puts "#{helper_file} has not been sporked. Run spork --bootstrap to do so."
116
- # are we in a rails app?
117
- if using_rails?
118
- puts "Preloading Rails environment"
119
- require "config/environment.rb"
120
- else
121
- puts "There's nothing I can really do for you. Bailing."
122
- return false
117
+ Spork.exec_prefork do
118
+ unless bootstrapped?
119
+ stderr.puts "#{helper_file} has not been bootstrapped. Run spork --bootstrap to do so."
120
+ stderr.flush
121
+
122
+ if framework.bootstrap_required?
123
+ stderr.puts "I can't do anything for you by default for the framework your using: #{framework.short_name}.\nYou must bootstrap #{helper_file} to continue."
124
+ stderr.flush
125
+ return false
126
+ end
123
127
  end
128
+
129
+ framework.preload do
130
+ if bootstrapped?
131
+ stderr.puts "Loading Spork.prefork block..."
132
+ stderr.flush
133
+ load(helper_file)
134
+ end
135
+ end
124
136
  end
125
137
  true
126
138
  end
@@ -130,7 +142,8 @@ class Spork::Server
130
142
  end
131
143
 
132
144
  def restart
133
- puts "restarting"
145
+ stderr.puts "restarting"
146
+ stderr.flush
134
147
  config = ::Config::CONFIG
135
148
  ruby = File::join(config['bindir'], config['ruby_install_name']) + config['EXEEXT']
136
149
  command_line = [ruby, $0, ARGV].flatten.join(' ')
@@ -144,7 +157,8 @@ class Spork::Server
144
157
  def sig_int_received
145
158
  if running?
146
159
  abort
147
- puts "Running tests stopped. Press CTRL-C again to stop the server."
160
+ stderr.puts "Running tests stopped. Press CTRL-C again to stop the server."
161
+ stderr.flush
148
162
  else
149
163
  exit!(0)
150
164
  end
data/lib/spork.rb CHANGED
@@ -1,46 +1,51 @@
1
1
  $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
2
2
  module Spork
3
3
  class << self
4
- def already_preforked
5
- @already_preforked ||= []
4
+ def already_ran
5
+ @already_ran ||= []
6
6
  end
7
7
 
8
- def already_run
9
- @already_run ||= []
8
+ def each_run_procs
9
+ @each_run_procs ||= []
10
10
  end
11
11
 
12
12
  def prefork(&block)
13
- return if already_preforked.include?(expanded_caller(caller.first))
14
- already_preforked << expanded_caller(caller.first)
13
+ return if already_ran?(caller.first)
15
14
  yield
16
15
  end
17
-
16
+
18
17
  def each_run(&block)
19
- return if @state == :preforking || (@state != :not_using_spork && already_run.include?(expanded_caller(caller.first)))
20
- already_run << expanded_caller(caller.first)
21
- yield
18
+ return if already_ran?(caller.first)
19
+ if @state == :using_spork
20
+ each_run_procs << block
21
+ else
22
+ yield
23
+ end
22
24
  end
23
-
24
- def preforking!
25
- @state = :preforking
25
+
26
+ def already_ran?(caller_script_and_line)
27
+ return true if already_ran.include?(expanded_caller(caller_script_and_line))
28
+ already_ran << expanded_caller(caller_script_and_line)
29
+ false
26
30
  end
27
-
28
- def running!
29
- @state = :running
31
+
32
+ def using_spork!
33
+ @state = :using_spork
30
34
  end
31
-
35
+
32
36
  def state
33
37
  @state ||= :not_using_spork
34
38
  end
35
-
36
- def exec_prefork(helper_file)
37
- preforking!
38
- load(helper_file)
39
+
40
+ def exec_prefork(&block)
41
+ using_spork!
42
+ yield
39
43
  end
40
-
41
- def exec_each_run(helper_file)
42
- running!
43
- load(helper_file)
44
+
45
+ def exec_each_run(&block)
46
+ each_run_procs.each { |p| p.call }
47
+ each_run_procs.clear
48
+ yield if block_given?
44
49
  end
45
50
 
46
51
  def expanded_caller(caller_line)
@@ -48,5 +53,29 @@ module Spork
48
53
  line.gsub(/:.+/, '')
49
54
  File.expand_path(Dir.pwd, file) + ":" + line
50
55
  end
56
+
57
+ def trap_method(klass, method_name)
58
+ klass.class_eval <<-EOF, __FILE__, __LINE__ + 1
59
+ alias :#{method_name}_without_spork :#{method_name} unless method_defined?(:#{method_name}_without_spork)
60
+ def #{method_name}(*args)
61
+ Spork.each_run_procs << lambda do
62
+ #{method_name}_without_spork(*args)
63
+ end
64
+ end
65
+ EOF
66
+ end
67
+
68
+ def trap_class_method(klass, method_name)
69
+ klass.class_eval <<-EOF, __FILE__, __LINE__ + 1
70
+ class << self
71
+ alias :#{method_name}_without_spork :#{method_name} unless method_defined?(:#{method_name}_without_spork)
72
+ def #{method_name}(*args)
73
+ Spork.each_run_procs << lambda do
74
+ #{method_name}_without_spork(*args)
75
+ end
76
+ end
77
+ end
78
+ EOF
79
+ end
51
80
  end
52
81
  end
data/spec/spec_helper.rb CHANGED
@@ -1,20 +1,102 @@
1
1
  require 'rubygems'
2
2
  require 'spec'
3
3
 
4
- $LOAD_PATH.unshift(File.dirname(__FILE__))
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
- SPEC_TMP_DIR = File.dirname(__FILE__) + "/tmp"
7
- require 'spork'
8
- require 'spork/runner.rb'
9
- require 'spork/server.rb'
10
- require 'stringio'
11
-
12
- Spec::Runner.configure do |config|
13
- config.before(:each) do
14
- $test_stdout = StringIO.new
4
+ unless $spec_helper_loaded
5
+ $spec_helper_loaded = true
6
+
7
+ $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
8
+ SPEC_TMP_DIR = File.expand_path('tmp', File.dirname(__FILE__))
9
+
10
+ require 'spork'
11
+ require 'spork/runner.rb'
12
+ require 'spork/server.rb'
13
+ require 'spork/diagnoser.rb'
14
+ require 'stringio'
15
+ require 'fileutils'
16
+
17
+ Spec::Runner.configure do |config|
18
+ config.before(:each) do
19
+ $test_stdout = StringIO.new
20
+ $test_stderr = StringIO.new
21
+ end
22
+
23
+ config.after(:each) do
24
+ FileUtils.rm_rf(SPEC_TMP_DIR) if File.directory?(SPEC_TMP_DIR)
25
+
26
+ end
27
+
28
+ def create_file(filename, contents)
29
+ FileUtils.mkdir_p(SPEC_TMP_DIR) unless File.directory?(SPEC_TMP_DIR)
30
+
31
+ in_current_dir do
32
+ FileUtils.mkdir_p(File.dirname(filename))
33
+ File.open(filename, 'wb') { |f| f << contents }
34
+ end
35
+ end
36
+
37
+ def in_current_dir(&block)
38
+ Dir.chdir(current_dir, &block)
39
+ end
40
+
41
+ def current_dir
42
+ @current_dir ||= SPEC_TMP_DIR
43
+ end
44
+ end
45
+
46
+
47
+ module Spec
48
+ module Matchers
49
+ class IncludeAStringLike
50
+ def initialize(substring_or_regex)
51
+ case substring_or_regex
52
+ when String
53
+ @regex = Regexp.new(Regexp.escape(substring_or_regex))
54
+ when Regexp
55
+ @regex = substring_or_regex
56
+ else
57
+ raise ArgumentError, "don't know what to do with the #{substring_or_regex.class} you provided"
58
+ end
59
+ end
60
+
61
+ def matches?(list_of_strings)
62
+ @list_of_strings = list_of_strings
63
+ @list_of_strings.any? { |s| s =~ @regex }
64
+ end
65
+ def failure_message
66
+ "#{@list_of_strings.inspect} expected to include a string like #{@regex.inspect}"
67
+ end
68
+ def negative_failure_message
69
+ "#{@list_of_strings.inspect} expected to not include a string like #{@regex.inspect}, but did"
70
+ end
71
+ end
72
+
73
+ def include_a_string_like(substring_or_regex)
74
+ IncludeAStringLike.new(substring_or_regex)
75
+ end
76
+ end
15
77
  end
78
+
79
+ module Spork::TestIOStreams
80
+ def self.included(klass)
81
+ klass.send(:extend, ::Spork::TestIOStreams::ClassMethods)
82
+ end
83
+
84
+ def stderr
85
+ self.class.stderr
86
+ end
87
+
88
+ def stdout
89
+ self.class.stdout
90
+ end
91
+
92
+ module ClassMethods
93
+ def stderr
94
+ $test_stderr
95
+ end
16
96
 
17
- config.after(:each) do
18
- FileUtils.rm_rf(SPEC_TMP_DIR)
97
+ def stdout
98
+ $test_stdout
99
+ end
100
+ end
19
101
  end
20
102
  end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ Spork::AppFramework[:Rails]
4
+
5
+ describe Spork::AppFramework::Rails do
6
+ describe ".version" do
7
+ it "detects the current version of rails" do
8
+ create_file("config/environment.rb", "RAILS_GEM_VERSION = '2.1.0'")
9
+ in_current_dir do
10
+ Spork::AppFramework::Rails.new.version.should == "2.1.0"
11
+ end
12
+
13
+ create_file("config/environment.rb", 'RAILS_GEM_VERSION = "2.1.0"')
14
+ in_current_dir do
15
+ Spork::AppFramework::Rails.new.version.should == "2.1.0"
16
+ end
17
+
18
+ create_file("config/environment.rb", 'RAILS_GEM_VERSION = "> 2.1.0"')
19
+ in_current_dir do
20
+ Spork::AppFramework::Rails.new.version.should == "> 2.1.0"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Spork::AppFramework do
4
+ describe ".detect_framework" do
5
+ it "detects when rails is installed and available" do
6
+ create_file("config/environment.rb", "RAILS_GEM_VERSION = '2.1.0'")
7
+ in_current_dir do
8
+ Spork::AppFramework.detect_framework.name.should == "Rails"
9
+ end
10
+ end
11
+
12
+ it "returns Unknown when no framework known detected" do
13
+ Spork::AppFramework.detect_framework.name.should == "Unknown"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,91 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+
4
+ describe Spork::Diagnoser do
5
+ after(:each) do
6
+ Spork::Diagnoser.remove_hook!
7
+ Spork::Diagnoser.loaded_files.clear
8
+ end
9
+
10
+ def run_simulation(directory, filename = nil, contents = nil, &block)
11
+ FileUtils.mkdir_p(directory)
12
+ Dir.chdir(directory) do
13
+ if filename
14
+ File.open(filename, 'wb') { |f| f << contents }
15
+ Spork::Diagnoser.install_hook!
16
+ require filename
17
+ end
18
+ yield if block_given?
19
+ end
20
+ end
21
+
22
+ it "installs it's hook and tells you when files have been loaded" do
23
+ run_simulation(SPEC_TMP_DIR, 'my_awesome_library_include.rb', '1 + 5')
24
+ Spork::Diagnoser.loaded_files.keys.should include_a_string_like('my_awesome_library_include')
25
+
26
+ end
27
+
28
+ it 'excludes files outside of Dir.pwd' do
29
+ run_simulation(SPEC_TMP_DIR + '/project_root', '../external_dependency.rb', '1 + 5')
30
+ Spork::Diagnoser.loaded_files.keys.should_not include_a_string_like('external_dependency')
31
+
32
+ end
33
+
34
+ it "excludes files outside of Dir.pwd but in ruby's include path" do
35
+ directory = SPEC_TMP_DIR + '/project_root'
36
+ external_dependency_dir = SPEC_TMP_DIR + '/external_dependency'
37
+ $: << external_dependency_dir
38
+ FileUtils.mkdir_p(directory)
39
+ FileUtils.mkdir_p(external_dependency_dir)
40
+ Dir.chdir(directory) do
41
+ File.open(external_dependency_dir + '/the_most_awesome_external_dependency_ever.rb', 'wb') { |f| f << 'funtimes = true' }
42
+ Spork::Diagnoser.install_hook!
43
+ require 'the_most_awesome_external_dependency_ever'
44
+ end
45
+
46
+ Spork::Diagnoser.loaded_files.keys.should_not include_a_string_like('the_most_awesome_external_dependency_ever')
47
+ $:.pop
48
+ end
49
+
50
+ it "expands files to their fully their fully qualified path" do
51
+ directory = SPEC_TMP_DIR + '/project_root'
52
+ lib_directory = directory + '/lib'
53
+ $: << lib_directory
54
+ FileUtils.mkdir_p(lib_directory)
55
+ Dir.chdir(directory) do
56
+ File.open(lib_directory + "/the_most_awesome_lib_file_ever.rb", "wb") { |f| f << "funtimes = true" }
57
+ Spork::Diagnoser.install_hook!
58
+ require 'the_most_awesome_lib_file_ever'
59
+ end
60
+
61
+ Spork::Diagnoser.loaded_files.keys.should include_a_string_like('lib/the_most_awesome_lib_file_ever')
62
+ $:.pop
63
+ end
64
+
65
+ it "can tell the difference between a folder in the project path and a file in an external path" do
66
+ directory = SPEC_TMP_DIR + '/project_root'
67
+ external_dependency_dir = SPEC_TMP_DIR + '/external_dependency'
68
+ $: << external_dependency_dir
69
+ FileUtils.mkdir_p(directory)
70
+ FileUtils.mkdir_p(external_dependency_dir)
71
+ Dir.chdir(directory) do
72
+ FileUtils.mkdir_p(directory + '/a_popular_folder_name')
73
+ File.open(external_dependency_dir + '/a_popular_folder_name.rb', 'wb') { |f| f << 'funtimes = true' }
74
+ Spork::Diagnoser.install_hook!
75
+ require 'a_popular_folder_name'
76
+ end
77
+
78
+ Spork::Diagnoser.loaded_files.keys.should_not include_a_string_like('a_popular_folder_name')
79
+ $:.pop
80
+ end
81
+
82
+ it "outputs the results relative to the current directory" do
83
+ Spork::Diagnoser.loaded_files["/project_path/lib/file.rb"] = "/project_path/lib/parent_file.rb:35"
84
+ Dir.stub!(:pwd).and_return("/project_path")
85
+ out = StringIO.new
86
+ Spork::Diagnoser.output_results(out)
87
+ out.string.should =~ %r([^/]lib/file.rb)
88
+ out.string.should =~ %r([^/]lib/parent_file.rb)
89
+ out.string.should_not include("/project_path/")
90
+ end
91
+ end
@@ -11,7 +11,7 @@ describe Spork::Runner do
11
11
 
12
12
  it "shows an error message if no matching server was found" do
13
13
  Spork::Runner.new(["argle_bargle"], @out, @err).run.should == false
14
- @out.string.should include(%("argle_bargle" didn't match a supported test framework))
14
+ @err.string.should include(%("argle_bargle" didn't match a supported test framework))
15
15
  end
16
16
 
17
17
  it "defaults to use rspec over cucumber" do
@@ -44,7 +44,7 @@ describe Spork::Runner do
44
44
  Spork::Server::RSpec.should_receive(:preload).and_return(true)
45
45
  Spork::Server::RSpec.should_receive(:run).and_return(true)
46
46
  Spork::Runner.new(['rspec'], @out, @err).run
47
- @out.string.should include("Using RSpec")
47
+ @err.string.should include("Using RSpec")
48
48
  end
49
49
 
50
50
  it "outputs a list of supported servers, along with supported asterisk" do
@@ -1,7 +1,11 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
 
3
+
3
4
  class FakeServer < Spork::Server
4
5
  attr_accessor :wait_time
6
+
7
+ include Spork::TestIOStreams
8
+
5
9
  def self.helper_file
6
10
  SPEC_TMP_DIR + "/fake/test_helper.rb"
7
11
  end
@@ -10,14 +14,6 @@ class FakeServer < Spork::Server
10
14
  1000
11
15
  end
12
16
 
13
- def self.puts(string)
14
- $test_stdout.puts(string)
15
- end
16
-
17
- def puts(string)
18
- $test_stdout.puts(string)
19
- end
20
-
21
17
  def run_tests(argv, input, output)
22
18
  sleep(@wait_time || 0.5)
23
19
  true
@@ -58,8 +54,7 @@ describe Spork::Server do
58
54
 
59
55
  describe "a fake server" do
60
56
  def create_helper_file
61
- FileUtils.mkdir_p(File.dirname(FakeServer.helper_file))
62
- FileUtils.touch(FakeServer.helper_file)
57
+ create_file(FakeServer.helper_file, "# stub spec helper file")
63
58
  end
64
59
 
65
60
  before(:each) do
@@ -94,9 +89,9 @@ describe Spork::Server do
94
89
  create_helper_file
95
90
  FakeServer.bootstrap
96
91
 
97
- $test_stdout.string.should include("Bootstrapping")
98
- $test_stdout.string.should include("Edit")
99
- $test_stdout.string.should include("favorite text editor")
92
+ $test_stderr.string.should include("Bootstrapping")
93
+ $test_stderr.string.should include("Edit")
94
+ $test_stderr.string.should include("favorite text editor")
100
95
 
101
96
  File.read(FakeServer.helper_file).should include(File.read(FakeServer::BOOTSTRAP_FILE))
102
97
  end
data/spec/spork_spec.rb CHANGED
@@ -3,8 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
  Spork.class_eval do
4
4
  def self.reset!
5
5
  @state = nil
6
- @already_run = nil
7
- @already_preforked = nil
6
+ @already_ran = nil
8
7
  end
9
8
  end
10
9
 
@@ -14,28 +13,28 @@ describe Spork do
14
13
  end
15
14
 
16
15
  def spec_helper_simulator
17
- ran = []
16
+ @ran ||= []
18
17
  Spork.prefork do
19
- ran << :prefork
18
+ @ran << :prefork
20
19
  end
21
20
 
22
21
  Spork.each_run do
23
- ran << :each_run
22
+ @ran << :each_run
24
23
  end
25
- ran
24
+ @ran
26
25
  end
27
26
 
28
27
  it "only runs the preload block when preforking" do
29
- ran = []
30
- Spork.preforking!
31
- spec_helper_simulator.should == [:prefork]
28
+ Spork.exec_prefork { spec_helper_simulator }
29
+ @ran.should == [:prefork]
32
30
  end
33
31
 
34
32
  it "only runs the each_run block when running" do
35
- Spork.preforking!
36
- spec_helper_simulator
37
- Spork.running!
38
- spec_helper_simulator.should == [:each_run]
33
+ Spork.exec_prefork { spec_helper_simulator }
34
+ @ran.should == [:prefork]
35
+
36
+ Spork.exec_each_run
37
+ @ran.should == [:prefork, :each_run]
39
38
  end
40
39
 
41
40
  it "runs both blocks when Spork not activated" do
@@ -43,8 +42,12 @@ describe Spork do
43
42
  end
44
43
 
45
44
  it "prevents blocks from being ran twice" do
46
- spec_helper_simulator.should == [:prefork, :each_run]
47
- spec_helper_simulator.should == []
45
+ Spork.exec_prefork { spec_helper_simulator }
46
+ Spork.exec_each_run
47
+ @ran.clear
48
+ Spork.exec_prefork { spec_helper_simulator }
49
+ Spork.exec_each_run
50
+ @ran.should == []
48
51
  end
49
52
 
50
53
  it "runs multiple prefork and each_run blocks at different locations" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timcharper-spork
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Harper
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-01 00:00:00 -07:00
12
+ date: 2009-06-05 00:00:00 -07:00
13
13
  default_executable: spork
14
14
  dependencies: []
15
15
 
@@ -27,13 +27,29 @@ files:
27
27
  - MIT-LICENSE
28
28
  - README.rdoc
29
29
  - assets/bootstrap.rb
30
+ - features/diagnostic_mode.feature
31
+ - features/rails_integration.feature
32
+ - features/steps/rails_steps.rb
33
+ - features/steps/sandbox_steps.rb
34
+ - features/support/env.rb
30
35
  - lib/spork.rb
36
+ - lib/spork/app_framework.rb
37
+ - lib/spork/app_framework/rails.rb
38
+ - lib/spork/app_framework/rails_stub_files/application.rb
39
+ - lib/spork/app_framework/rails_stub_files/application_controller.rb
40
+ - lib/spork/app_framework/rails_stub_files/application_helper.rb
41
+ - lib/spork/app_framework/unknown.rb
42
+ - lib/spork/custom_io_streams.rb
43
+ - lib/spork/diagnoser.rb
31
44
  - lib/spork/forker.rb
32
45
  - lib/spork/runner.rb
33
46
  - lib/spork/server.rb
34
47
  - lib/spork/server/cucumber.rb
35
48
  - lib/spork/server/rspec.rb
36
49
  - spec/spec_helper.rb
50
+ - spec/spork/app_framework/rails_spec.rb
51
+ - spec/spork/app_framework_spec.rb
52
+ - spec/spork/diagnoser_spec.rb
37
53
  - spec/spork/forker_spec.rb
38
54
  - spec/spork/runner_spec.rb
39
55
  - spec/spork/server/rspec_spec.rb
@@ -68,6 +84,9 @@ specification_version: 2
68
84
  summary: spork
69
85
  test_files:
70
86
  - spec/spec_helper.rb
87
+ - spec/spork/app_framework/rails_spec.rb
88
+ - spec/spork/app_framework_spec.rb
89
+ - spec/spork/diagnoser_spec.rb
71
90
  - spec/spork/forker_spec.rb
72
91
  - spec/spork/runner_spec.rb
73
92
  - spec/spork/server/rspec_spec.rb