timcharper-spork 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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