spork 0.4.4 → 0.5.1

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,81 @@
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 the following code appears in "config/environment.rb" after /Rails::Initializer.run/:
53
+ """
54
+ config.active_record.observers = :user_observer
55
+ """
56
+ And a file named "spec/models/user_spec.rb" with:
57
+ """
58
+ describe User do
59
+ it "does absoluately nothing" do
60
+ Spork.state.should == :using_spork
61
+ $loaded_stuff.should include('establish_connection')
62
+ $loaded_stuff.should include('User')
63
+ $loaded_stuff.should include('UserObserver')
64
+ $loaded_stuff.should include('ApplicationHelper')
65
+ puts "Specs successfully run within spork, and all initialization files were loaded"
66
+ end
67
+ end
68
+ """
69
+ Scenario: Analyzing files were preloaded
70
+ When I run spork --diagnose
71
+ Then the output should not contain "user_observer.rb"
72
+ Then the output should not contain "user.rb"
73
+ Then the output should not contain "app/controllers/application.rb"
74
+ Then the output should not contain "app/controllers/application_controller.rb"
75
+ Then the output should not contain "app/controllers/application_helper.rb"
76
+
77
+ Scenario: Running spork with a rails app and observers
78
+
79
+ When I fire up a spork instance with "spork rspec"
80
+ And I run spec --drb spec/models/user_spec.rb
81
+ Then the output should contain "Specs successfully run within spork, and all initialization files were loaded"
@@ -0,0 +1,7 @@
1
+ Given /^I am in a fresh rails project named "(.+)"$/ do |folder_name|
2
+ @current_dir = SporkWorld::SANDBOX_DIR
3
+ version_argument = ENV['RAILS_VERSION'] ? "_#{ENV['RAILS_VERSION']}_" : nil
4
+ # run("#{SporkWorld::RUBY_BINARY} #{%x{which rails}.chomp} #{folder_name}")
5
+ run([SporkWorld::RUBY_BINARY, %x{which rails}.chomp, version_argument, folder_name].compact * " ")
6
+ @current_dir = File.join(File.join(SporkWorld::SANDBOX_DIR, folder_name))
7
+ end
@@ -0,0 +1,78 @@
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
+ # the following code appears in "config/environment.rb" after /Rails::Initializer.run/:
16
+ Given /^the following code appears in "([^\"]*)" after \/([^\\\/]*)\/:$/ do |file_name, regex, content|
17
+ # require 'ruby-debug'; Debugger.start; Debugger.start_control; debugger
18
+
19
+ regex = Regexp.new(regex)
20
+ in_current_dir do
21
+ content_lines = File.read(file_name).split("\n")
22
+ 0.upto(content_lines.length - 1) do |line_index|
23
+ if regex.match(content_lines[line_index])
24
+ puts "found: #{content_lines[line_index]}"
25
+ content_lines.insert(line_index + 1, content)
26
+ break
27
+ end
28
+ end
29
+ File.open(file_name, 'wb') { |f| f << (content_lines * "\n") }
30
+ end
31
+ end
32
+
33
+ When /^I run (spork|spec)($| .*$)/ do |command, spork_opts|
34
+ if command == 'spork'
35
+ command = SporkWorld::BINARY
36
+ else
37
+ command = %x{which #{command}}.chomp
38
+ end
39
+ run "#{SporkWorld::RUBY_BINARY} #{command} #{spork_opts}"
40
+ end
41
+
42
+ When /^I fire up a spork instance with "spork(.*)"$/ do |spork_opts|
43
+ run_in_background "#{SporkWorld::RUBY_BINARY} #{SporkWorld::BINARY} #{spork_opts}"
44
+ output = ""
45
+ begin
46
+ status = Timeout::timeout(15) do
47
+ # Something that should be interrupted if it takes too much time...
48
+ while line = @bg_stderr.gets
49
+ output << line
50
+ puts line
51
+ break if line.include?("Spork is ready and listening")
52
+ end
53
+ end
54
+ rescue Timeout::Error
55
+ puts "I can't seem to launch Spork properly. Output was:\n#{output}"
56
+ true.should == false
57
+ end
58
+ end
59
+
60
+ Then /^the output should contain$/ do |text|
61
+ last_stdout.should include(text)
62
+ end
63
+
64
+ Then /^the output should contain "(.+)"$/ do |text|
65
+ last_stdout.should include(text)
66
+ end
67
+
68
+ Then /^the output should not contain$/ do |text|
69
+ last_stdout.should_not include(text)
70
+ end
71
+
72
+ Then /^the output should not contain "(.+)"$/ do |text|
73
+ last_stdout.should_not include(text)
74
+ end
75
+
76
+ Then /^the output should be$/ do |text|
77
+ last_stdout.should == text
78
+ 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
@@ -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
@@ -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