mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge commit 'rails/master'
This commit is contained in:
commit
97aba353c8
15 changed files with 64 additions and 809 deletions
|
@ -50,7 +50,7 @@ module ActionController
|
|||
|
||||
def new
|
||||
# DEPRECATE Rails application fallback
|
||||
Rails.application
|
||||
Rails.application.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'stringio'
|
||||
require 'fileutils'
|
||||
require 'fcgi_handler'
|
||||
|
||||
def message(s)
|
||||
$stderr.puts "listener: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
|
||||
end
|
||||
|
||||
class RemoteCGI < CGI
|
||||
attr_accessor :stdinput, :stdoutput, :env_table
|
||||
def initialize(env_table, input = nil, output = nil)
|
||||
self.env_table = env_table
|
||||
self.stdinput = input || StringIO.new
|
||||
self.stdoutput = output || StringIO.new
|
||||
super()
|
||||
end
|
||||
|
||||
def out(stream) # Ignore the requested output stream
|
||||
super(stdoutput)
|
||||
end
|
||||
end
|
||||
|
||||
class Listener
|
||||
include DRbUndumped
|
||||
|
||||
def initialize(timeout, socket_path)
|
||||
@socket = File.expand_path(socket_path)
|
||||
@mutex = Mutex.new
|
||||
@active = false
|
||||
@timeout = timeout
|
||||
|
||||
@handler = RailsFCGIHandler.new
|
||||
@handler.extend DRbUndumped
|
||||
|
||||
message 'opening socket'
|
||||
DRb.start_service("drbunix:#{@socket}", self)
|
||||
|
||||
message 'entering process loop'
|
||||
@handler.process! self
|
||||
end
|
||||
|
||||
def each_cgi(&cgi_block)
|
||||
@cgi_block = cgi_block
|
||||
message 'entering idle loop'
|
||||
loop do
|
||||
sleep @timeout rescue nil
|
||||
die! unless @active
|
||||
@active = false
|
||||
end
|
||||
end
|
||||
|
||||
def process(env, input)
|
||||
message 'received request'
|
||||
@mutex.synchronize do
|
||||
@active = true
|
||||
|
||||
message 'creating input stream'
|
||||
input_stream = StringIO.new(input)
|
||||
message 'building CGI instance'
|
||||
cgi = RemoteCGI.new(eval(env), input_stream)
|
||||
|
||||
message 'yielding to fcgi handler'
|
||||
@cgi_block.call cgi
|
||||
message 'yield finished -- sending output'
|
||||
|
||||
cgi.stdoutput.seek(0)
|
||||
output = cgi.stdoutput.read
|
||||
|
||||
return output
|
||||
end
|
||||
end
|
||||
|
||||
def die!
|
||||
message 'shutting down'
|
||||
DRb.stop_service
|
||||
FileUtils.rm_f @socket
|
||||
Kernel.exit 0
|
||||
end
|
||||
end
|
||||
|
||||
socket_path = ARGV.shift
|
||||
timeout = (ARGV.shift || 90).to_i
|
||||
|
||||
Listener.new(timeout, socket_path)
|
|
@ -1,239 +0,0 @@
|
|||
require 'fcgi'
|
||||
require 'logger'
|
||||
require 'rails/dispatcher'
|
||||
require 'rbconfig'
|
||||
|
||||
class RailsFCGIHandler
|
||||
SIGNALS = {
|
||||
'HUP' => :reload,
|
||||
'INT' => :exit_now,
|
||||
'TERM' => :exit_now,
|
||||
'USR1' => :exit,
|
||||
'USR2' => :restart
|
||||
}
|
||||
GLOBAL_SIGNALS = SIGNALS.keys - %w(USR1)
|
||||
|
||||
attr_reader :when_ready
|
||||
|
||||
attr_accessor :log_file_path
|
||||
attr_accessor :gc_request_period
|
||||
|
||||
# Initialize and run the FastCGI instance, passing arguments through to new.
|
||||
def self.process!(*args, &block)
|
||||
new(*args, &block).process!
|
||||
end
|
||||
|
||||
# Initialize the FastCGI instance with the path to a crash log
|
||||
# detailing unhandled exceptions (default RAILS_ROOT/log/fastcgi.crash.log)
|
||||
# and the number of requests to process between garbage collection runs
|
||||
# (default nil for normal GC behavior.) Optionally, pass a block which
|
||||
# takes this instance as an argument for further configuration.
|
||||
def initialize(log_file_path = nil, gc_request_period = nil)
|
||||
self.log_file_path = log_file_path || "#{RAILS_ROOT}/log/fastcgi.crash.log"
|
||||
self.gc_request_period = gc_request_period
|
||||
|
||||
# Yield for additional configuration.
|
||||
yield self if block_given?
|
||||
|
||||
# Safely install signal handlers.
|
||||
install_signal_handlers
|
||||
|
||||
@app = Dispatcher.new
|
||||
|
||||
# Start error timestamp at 11 seconds ago.
|
||||
@last_error_on = Time.now - 11
|
||||
end
|
||||
|
||||
def process!(provider = FCGI)
|
||||
mark_features!
|
||||
|
||||
dispatcher_log :info, 'starting'
|
||||
process_each_request provider
|
||||
dispatcher_log :info, 'stopping gracefully'
|
||||
|
||||
rescue Exception => error
|
||||
case error
|
||||
when SystemExit
|
||||
dispatcher_log :info, 'stopping after explicit exit'
|
||||
when SignalException
|
||||
dispatcher_error error, 'stopping after unhandled signal'
|
||||
else
|
||||
# Retry if exceptions occur more than 10 seconds apart.
|
||||
if Time.now - @last_error_on > 10
|
||||
@last_error_on = Time.now
|
||||
dispatcher_error error, 'retrying after unhandled exception'
|
||||
retry
|
||||
else
|
||||
dispatcher_error error, 'stopping after unhandled exception within 10 seconds of the last'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def process_each_request(provider)
|
||||
request = nil
|
||||
|
||||
catch :exit do
|
||||
provider.each do |request|
|
||||
process_request(request)
|
||||
|
||||
case when_ready
|
||||
when :reload
|
||||
reload!
|
||||
when :restart
|
||||
close_connection(request)
|
||||
restart!
|
||||
when :exit
|
||||
close_connection(request)
|
||||
throw :exit
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue SignalException => signal
|
||||
raise unless signal.message == 'SIGUSR1'
|
||||
close_connection(request)
|
||||
end
|
||||
|
||||
def process_request(request)
|
||||
@processing, @when_ready = true, nil
|
||||
gc_countdown
|
||||
|
||||
with_signal_handler 'USR1' do
|
||||
begin
|
||||
::Rack::Handler::FastCGI.serve(request, @app)
|
||||
rescue SignalException, SystemExit
|
||||
raise
|
||||
rescue Exception => error
|
||||
dispatcher_error error, 'unhandled dispatch error'
|
||||
end
|
||||
end
|
||||
ensure
|
||||
@processing = false
|
||||
end
|
||||
|
||||
def logger
|
||||
@logger ||= Logger.new(@log_file_path)
|
||||
end
|
||||
|
||||
def dispatcher_log(level, msg)
|
||||
time_str = Time.now.strftime("%d/%b/%Y:%H:%M:%S")
|
||||
logger.send(level, "[#{time_str} :: #{$$}] #{msg}")
|
||||
rescue Exception => log_error # Logger errors
|
||||
STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n"
|
||||
STDERR << " #{log_error.class}: #{log_error.message}\n"
|
||||
end
|
||||
|
||||
def dispatcher_error(e, msg = "")
|
||||
error_message =
|
||||
"Dispatcher failed to catch: #{e} (#{e.class})\n" +
|
||||
" #{e.backtrace.join("\n ")}\n#{msg}"
|
||||
dispatcher_log(:error, error_message)
|
||||
end
|
||||
|
||||
def install_signal_handlers
|
||||
GLOBAL_SIGNALS.each { |signal| install_signal_handler(signal) }
|
||||
end
|
||||
|
||||
def install_signal_handler(signal, handler = nil)
|
||||
if SIGNALS.include?(signal) && self.class.method_defined?(name = "#{SIGNALS[signal]}_handler")
|
||||
handler ||= method(name).to_proc
|
||||
|
||||
begin
|
||||
trap(signal, handler)
|
||||
rescue ArgumentError
|
||||
dispatcher_log :warn, "Ignoring unsupported signal #{signal}."
|
||||
end
|
||||
else
|
||||
dispatcher_log :warn, "Ignoring unsupported signal #{signal}."
|
||||
end
|
||||
end
|
||||
|
||||
def with_signal_handler(signal)
|
||||
install_signal_handler(signal)
|
||||
yield
|
||||
ensure
|
||||
install_signal_handler(signal, 'DEFAULT')
|
||||
end
|
||||
|
||||
def exit_now_handler(signal)
|
||||
dispatcher_log :info, "asked to stop immediately"
|
||||
exit
|
||||
end
|
||||
|
||||
def exit_handler(signal)
|
||||
dispatcher_log :info, "asked to stop ASAP"
|
||||
if @processing
|
||||
@when_ready = :exit
|
||||
else
|
||||
throw :exit
|
||||
end
|
||||
end
|
||||
|
||||
def reload_handler(signal)
|
||||
dispatcher_log :info, "asked to reload ASAP"
|
||||
if @processing
|
||||
@when_ready = :reload
|
||||
else
|
||||
reload!
|
||||
end
|
||||
end
|
||||
|
||||
def restart_handler(signal)
|
||||
dispatcher_log :info, "asked to restart ASAP"
|
||||
if @processing
|
||||
@when_ready = :restart
|
||||
else
|
||||
restart!
|
||||
end
|
||||
end
|
||||
|
||||
def restart!
|
||||
config = ::Config::CONFIG
|
||||
ruby = File::join(config['bindir'], config['ruby_install_name']) + config['EXEEXT']
|
||||
command_line = [ruby, $0, ARGV].flatten.join(' ')
|
||||
|
||||
dispatcher_log :info, "restarted"
|
||||
|
||||
# close resources as they won't be closed by
|
||||
# the OS when using exec
|
||||
logger.close rescue nil
|
||||
Rails.logger.close rescue nil
|
||||
|
||||
exec(command_line)
|
||||
end
|
||||
|
||||
def reload!
|
||||
run_gc! if gc_request_period
|
||||
restore!
|
||||
@when_ready = nil
|
||||
dispatcher_log :info, "reloaded"
|
||||
end
|
||||
|
||||
# Make a note of $" so we can safely reload this instance.
|
||||
def mark_features!
|
||||
@features = $".clone
|
||||
end
|
||||
|
||||
def restore!
|
||||
$".replace @features
|
||||
Dispatcher.reset_application!
|
||||
ActionController::Routing::Routes.reload
|
||||
end
|
||||
|
||||
def run_gc!
|
||||
@gc_request_countdown = gc_request_period
|
||||
GC.enable; GC.start; GC.disable
|
||||
end
|
||||
|
||||
def gc_countdown
|
||||
if gc_request_period
|
||||
@gc_request_countdown ||= gc_request_period
|
||||
@gc_request_countdown -= 1
|
||||
run_gc! if @gc_request_countdown <= 0
|
||||
end
|
||||
end
|
||||
|
||||
def close_connection(request)
|
||||
request.finish if request
|
||||
end
|
||||
end
|
|
@ -18,9 +18,6 @@ module Rails::Generators
|
|||
class_option :template, :type => :string, :aliases => "-m",
|
||||
:desc => "Path to an application template (can be a filesystem path or URL)."
|
||||
|
||||
class_option :with_dispatchers, :type => :boolean, :aliases => "-D", :default => false,
|
||||
:desc => "Add CGI/FastCGI/mod_ruby dispatchers code"
|
||||
|
||||
class_option :skip_activerecord, :type => :boolean, :aliases => "-O", :default => false,
|
||||
:desc => "Skip ActiveRecord files"
|
||||
|
||||
|
@ -113,19 +110,6 @@ module Rails::Generators
|
|||
directory "public", "public", :recursive => false # Do small steps, so anyone can overwrite it.
|
||||
end
|
||||
|
||||
def create_dispatch_files
|
||||
return unless options[:with_dispatchers]
|
||||
|
||||
template "dispatchers/dispatch.rb", "public/dispatch.rb"
|
||||
chmod "public/dispatch.rb", 0755, :verbose => false
|
||||
|
||||
template "dispatchers/dispatch.rb", "public/dispatch.cgi"
|
||||
chmod "public/dispatch.cgi", 0755, :verbose => false
|
||||
|
||||
template "dispatchers/dispatch.fcgi", "public/dispatch.fcgi"
|
||||
chmod "public/dispatch.fcgi", 0755, :verbose => false
|
||||
end
|
||||
|
||||
def create_public_image_files
|
||||
directory "public/images"
|
||||
end
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<%= shebang %>
|
||||
#
|
||||
# You may specify the path to the FastCGI crash log (a log of unhandled
|
||||
# exceptions which forced the FastCGI instance to exit, great for debugging)
|
||||
# and the number of requests to process before running garbage collection.
|
||||
#
|
||||
# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log
|
||||
# and the GC period is nil (turned off). A reasonable number of requests
|
||||
# could range from 10-100 depending on the memory footprint of your app.
|
||||
#
|
||||
# Example:
|
||||
# # Default log path, normal GC behavior.
|
||||
# RailsFCGIHandler.process!
|
||||
#
|
||||
# # Default log path, 50 requests between GC.
|
||||
# RailsFCGIHandler.process! nil, 50
|
||||
#
|
||||
# # Custom log path, normal GC behavior.
|
||||
# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
|
||||
#
|
||||
require File.dirname(__FILE__) + "/../config/environment"
|
||||
require 'fcgi_handler'
|
||||
|
||||
RailsFCGIHandler.process!
|
|
@ -1,10 +0,0 @@
|
|||
<%= shebang %>
|
||||
|
||||
require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)
|
||||
|
||||
# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
|
||||
# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
|
||||
require "dispatcher"
|
||||
|
||||
ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
|
||||
Dispatcher.dispatch
|
|
@ -1,97 +0,0 @@
|
|||
<%= shebang %>
|
||||
|
||||
require 'drb'
|
||||
|
||||
# This file includes an experimental gateway CGI implementation. It will work
|
||||
# only on platforms which support both fork and sockets.
|
||||
#
|
||||
# To enable it edit public/.htaccess and replace dispatch.cgi with gateway.cgi.
|
||||
#
|
||||
# Next, create the directory log/drb_gateway and grant the apache user rw access
|
||||
# to said directory.
|
||||
#
|
||||
# On the next request to your server, the gateway tracker should start up, along
|
||||
# with a few listener processes. This setup should provide you with much better
|
||||
# speeds than dispatch.cgi.
|
||||
#
|
||||
# Keep in mind that the first request made to the server will be slow, as the
|
||||
# tracker and listeners will have to load. Also, the tracker and listeners will
|
||||
# shutdown after a period if inactivity. You can set this value below -- the
|
||||
# default is 90 seconds.
|
||||
|
||||
TrackerSocket = File.expand_path(File.join(File.dirname(__FILE__), '../log/drb_gateway/tracker.sock'))
|
||||
DieAfter = 90 # Seconds
|
||||
Listeners = 3
|
||||
|
||||
def message(s)
|
||||
$stderr.puts "gateway.cgi: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
|
||||
end
|
||||
|
||||
def listener_socket(number)
|
||||
File.expand_path(File.join(File.dirname(__FILE__), "../log/drb_gateway/listener_#{number}.sock"))
|
||||
end
|
||||
|
||||
unless File.exist? TrackerSocket
|
||||
message "Starting tracker and #{Listeners} listeners"
|
||||
fork do
|
||||
Process.setsid
|
||||
STDIN.reopen "/dev/null"
|
||||
STDOUT.reopen "/dev/null", "a"
|
||||
|
||||
root = File.expand_path(File.dirname(__FILE__) + '/..')
|
||||
|
||||
message "starting tracker"
|
||||
fork do
|
||||
ARGV.clear
|
||||
ARGV << TrackerSocket << Listeners.to_s << DieAfter.to_s
|
||||
load File.join(root, 'script', 'tracker')
|
||||
end
|
||||
|
||||
message "starting listeners"
|
||||
require File.join(root, 'config/environment.rb')
|
||||
Listeners.times do |number|
|
||||
fork do
|
||||
ARGV.clear
|
||||
ARGV << listener_socket(number) << DieAfter.to_s
|
||||
load File.join(root, 'script', 'listener')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
message "waiting for tracker and listener to arise..."
|
||||
ready = false
|
||||
10.times do
|
||||
sleep 0.5
|
||||
break if (ready = File.exist?(TrackerSocket) && File.exist?(listener_socket(0)))
|
||||
end
|
||||
|
||||
if ready
|
||||
message "tracker and listener are ready"
|
||||
else
|
||||
message "Waited 5 seconds, listener and tracker not ready... dropping request"
|
||||
Kernel.exit 1
|
||||
end
|
||||
end
|
||||
|
||||
DRb.start_service
|
||||
|
||||
message "connecting to tracker"
|
||||
tracker = DRbObject.new_with_uri("drbunix:#{TrackerSocket}")
|
||||
|
||||
input = $stdin.read
|
||||
$stdin.close
|
||||
|
||||
env = ENV.inspect
|
||||
|
||||
output = nil
|
||||
tracker.with_listener do |number|
|
||||
message "connecting to listener #{number}"
|
||||
socket = listener_socket(number)
|
||||
listener = DRbObject.new_with_uri("drbunix:#{socket}")
|
||||
output = listener.process(env, input)
|
||||
message "listener #{number} has finished, writing output"
|
||||
end
|
||||
|
||||
$stdout.write output
|
||||
$stdout.flush
|
||||
$stdout.close
|
|
@ -110,11 +110,6 @@ namespace :rails do
|
|||
invoke_from_app_generator :create_prototype_files
|
||||
end
|
||||
|
||||
desc "Generate dispatcher files in RAILS_ROOT/public"
|
||||
task :generate_dispatchers do
|
||||
invoke_from_app_generator :create_dispatch_files
|
||||
end
|
||||
|
||||
desc "Add new scripts to the application script/ directory"
|
||||
task :scripts do
|
||||
invoke_from_app_generator :create_script_files
|
||||
|
|
|
@ -25,11 +25,3 @@ if defined?(RAILS_ROOT)
|
|||
else
|
||||
RAILS_ROOT = File.dirname(__FILE__)
|
||||
end
|
||||
|
||||
def uses_gem(gem_name, test_name, version = '> 0')
|
||||
gem gem_name.to_s, version
|
||||
require gem_name.to_s
|
||||
yield
|
||||
rescue LoadError
|
||||
$stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again."
|
||||
end
|
||||
|
|
52
railties/test/application/console_test.rb
Normal file
52
railties/test/application/console_test.rb
Normal file
|
@ -0,0 +1,52 @@
|
|||
require 'isolation/abstract_unit'
|
||||
|
||||
class ConsoleTest < Test::Unit::TestCase
|
||||
include ActiveSupport::Testing::Isolation
|
||||
|
||||
def setup
|
||||
build_app
|
||||
boot_rails
|
||||
|
||||
# Load steps taken from rails/commands/console.rb
|
||||
require "#{rails_root}/config/environment"
|
||||
require 'rails/console_app'
|
||||
require 'rails/console_with_helpers'
|
||||
end
|
||||
|
||||
def test_app_method_should_return_integration_session
|
||||
console_session = app
|
||||
assert_not_nil console_session
|
||||
assert_instance_of ActionController::Integration::Session, console_session
|
||||
end
|
||||
|
||||
def test_new_session_should_return_integration_session
|
||||
session = new_session
|
||||
assert_not_nil session
|
||||
assert_instance_of ActionController::Integration::Session, session
|
||||
end
|
||||
|
||||
def test_reload_should_fire_preparation_callbacks
|
||||
a = b = c = nil
|
||||
|
||||
# TODO: These should be defined on the initializer
|
||||
ActionDispatch::Callbacks.to_prepare { a = b = c = 1 }
|
||||
ActionDispatch::Callbacks.to_prepare { b = c = 2 }
|
||||
ActionDispatch::Callbacks.to_prepare { c = 3 }
|
||||
|
||||
# Hide Reloading... output
|
||||
silence_stream(STDOUT) do
|
||||
reload!
|
||||
end
|
||||
|
||||
assert_equal 1, a
|
||||
assert_equal 2, b
|
||||
assert_equal 3, c
|
||||
end
|
||||
|
||||
def test_access_to_helpers
|
||||
assert_not_nil helper
|
||||
assert_instance_of ActionView::Base, helper
|
||||
assert_equal 'Once upon a time in a world...',
|
||||
helper.truncate('Once upon a time in a world far far away')
|
||||
end
|
||||
end
|
|
@ -43,6 +43,13 @@ module ApplicationTests
|
|||
assert Rails.application.new.is_a?(Rails::Application)
|
||||
end
|
||||
|
||||
# Passenger still uses AC::Dispatcher, so we need to
|
||||
# keep it working for now
|
||||
test "deprecated ActionController::Dispatcher still works" do
|
||||
rackup
|
||||
assert ActionController::Dispatcher.new.is_a?(Rails::Application)
|
||||
end
|
||||
|
||||
test "the config object is available on the application object" do
|
||||
rackup
|
||||
assert_equal 'UTC', Rails.application.config.time_zone
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
require 'action_controller' # console_app uses 'action_controller/integration'
|
||||
|
||||
require 'rails/dispatcher'
|
||||
require 'rails/console_app'
|
||||
|
||||
module Rails
|
||||
def self.application
|
||||
ActionController::Routing::Routes
|
||||
end
|
||||
end
|
||||
|
||||
# console_app sets Test::Unit.run to work around the at_exit hook in test/unit, which kills IRB
|
||||
if Test::Unit.respond_to?(:run=)
|
||||
Test::Unit.run = false
|
||||
|
||||
class ConsoleAppTest < Test::Unit::TestCase
|
||||
def test_app_method_should_return_integration_session
|
||||
assert_nothing_thrown do
|
||||
console_session = app
|
||||
assert_not_nil console_session
|
||||
assert_instance_of ActionController::Integration::Session,
|
||||
console_session
|
||||
end
|
||||
end
|
||||
|
||||
def test_reload_should_fire_preparation_callbacks
|
||||
a = b = c = nil
|
||||
|
||||
ActionDispatch::Callbacks.to_prepare { a = b = c = 1 }
|
||||
ActionDispatch::Callbacks.to_prepare { b = c = 2 }
|
||||
ActionDispatch::Callbacks.to_prepare { c = 3 }
|
||||
ActionController::Routing::Routes.expects(:reload)
|
||||
|
||||
reload!
|
||||
|
||||
assert_equal 1, a
|
||||
assert_equal 2, b
|
||||
assert_equal 3, c
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,268 +0,0 @@
|
|||
require 'abstract_unit'
|
||||
|
||||
uses_gem "fcgi", "0.8.7" do
|
||||
|
||||
require 'action_controller'
|
||||
require 'rails/fcgi_handler'
|
||||
|
||||
module Rails
|
||||
def self.application
|
||||
ActionController::Routing::Routes
|
||||
end
|
||||
end
|
||||
|
||||
class RailsFCGIHandlerTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@log = StringIO.new
|
||||
@handler = RailsFCGIHandler.new(@log)
|
||||
end
|
||||
|
||||
def test_process_restart
|
||||
request = mock
|
||||
FCGI.stubs(:each).yields(request)
|
||||
|
||||
@handler.expects(:process_request).once
|
||||
@handler.expects(:dispatcher_error).never
|
||||
|
||||
@handler.expects(:when_ready).returns(:restart)
|
||||
@handler.expects(:close_connection).with(request)
|
||||
@handler.expects(:reload!).never
|
||||
@handler.expects(:restart!)
|
||||
|
||||
@handler.process!
|
||||
end
|
||||
|
||||
def test_process_exit
|
||||
request = mock
|
||||
FCGI.stubs(:each).yields(request)
|
||||
|
||||
@handler.expects(:process_request).once
|
||||
@handler.expects(:dispatcher_error).never
|
||||
|
||||
@handler.expects(:when_ready).returns(:exit)
|
||||
@handler.expects(:close_connection).with(request)
|
||||
@handler.expects(:reload!).never
|
||||
@handler.expects(:restart!).never
|
||||
|
||||
@handler.process!
|
||||
end
|
||||
|
||||
def test_process_with_system_exit_exception
|
||||
request = mock
|
||||
FCGI.stubs(:each).yields(request)
|
||||
|
||||
@handler.expects(:process_request).once.raises(SystemExit)
|
||||
@handler.stubs(:dispatcher_log)
|
||||
@handler.expects(:dispatcher_log).with(:info, regexp_matches(/^stopping/))
|
||||
@handler.expects(:dispatcher_error).never
|
||||
|
||||
@handler.expects(:when_ready).never
|
||||
@handler.expects(:close_connection).never
|
||||
@handler.expects(:reload!).never
|
||||
@handler.expects(:restart!).never
|
||||
|
||||
@handler.process!
|
||||
end
|
||||
|
||||
def test_restart_handler_outside_request
|
||||
@handler.expects(:dispatcher_log).with(:info, "asked to restart ASAP")
|
||||
@handler.expects(:restart!).once
|
||||
|
||||
@handler.send(:restart_handler, nil)
|
||||
assert_equal nil, @handler.when_ready
|
||||
end
|
||||
|
||||
def test_install_signal_handler_should_log_on_bad_signal
|
||||
@handler.stubs(:trap).raises(ArgumentError)
|
||||
|
||||
@handler.expects(:dispatcher_log).with(:warn, "Ignoring unsupported signal CHEESECAKE.")
|
||||
@handler.send(:install_signal_handler, "CHEESECAKE", nil)
|
||||
end
|
||||
|
||||
def test_reload
|
||||
@handler.expects(:restore!)
|
||||
@handler.expects(:dispatcher_log).with(:info, "reloaded")
|
||||
|
||||
@handler.send(:reload!)
|
||||
assert_nil @handler.when_ready
|
||||
end
|
||||
|
||||
|
||||
def test_reload_runs_gc_when_gc_request_period_set
|
||||
@handler.expects(:run_gc!)
|
||||
@handler.expects(:restore!)
|
||||
@handler.expects(:dispatcher_log).with(:info, "reloaded")
|
||||
@handler.gc_request_period = 10
|
||||
@handler.send(:reload!)
|
||||
end
|
||||
|
||||
def test_reload_doesnt_run_gc_if_gc_request_period_isnt_set
|
||||
@handler.expects(:run_gc!).never
|
||||
@handler.expects(:restore!)
|
||||
@handler.expects(:dispatcher_log).with(:info, "reloaded")
|
||||
@handler.send(:reload!)
|
||||
end
|
||||
|
||||
def test_restart!
|
||||
@handler.expects(:dispatcher_log).with(:info, "restarted")
|
||||
@handler.expects(:exec).returns('restarted')
|
||||
assert_equal 'restarted', @handler.send(:restart!)
|
||||
end
|
||||
|
||||
def test_restore!
|
||||
$".expects(:replace)
|
||||
Dispatcher.expects(:reset_application!)
|
||||
ActionController::Routing::Routes.expects(:reload)
|
||||
@handler.send(:restore!)
|
||||
end
|
||||
|
||||
def test_uninterrupted_processing
|
||||
request = mock
|
||||
FCGI.expects(:each).yields(request)
|
||||
@handler.expects(:process_request).with(request)
|
||||
|
||||
@handler.process!
|
||||
|
||||
assert_nil @handler.when_ready
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase
|
||||
class ::RailsFCGIHandler
|
||||
attr_accessor :signal
|
||||
alias_method :old_gc_countdown, :gc_countdown
|
||||
def gc_countdown
|
||||
signal ? Process.kill(signal, $$) : old_gc_countdown
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@log = StringIO.new
|
||||
@handler = RailsFCGIHandler.new(@log)
|
||||
@dispatcher = mock
|
||||
Dispatcher.stubs(:new).returns(@dispatcher)
|
||||
end
|
||||
|
||||
def test_interrupted_via_HUP_when_not_in_request
|
||||
request = mock
|
||||
FCGI.expects(:each).once.yields(request)
|
||||
@handler.expects(:signal).times(2).returns('HUP')
|
||||
|
||||
@handler.expects(:reload!).once
|
||||
@handler.expects(:close_connection).never
|
||||
@handler.expects(:exit).never
|
||||
|
||||
@handler.process!
|
||||
assert_equal :reload, @handler.when_ready
|
||||
end
|
||||
|
||||
def test_interrupted_via_USR1_when_not_in_request
|
||||
request = mock
|
||||
FCGI.expects(:each).once.yields(request)
|
||||
@handler.expects(:signal).times(2).returns('USR1')
|
||||
@handler.expects(:exit_handler).never
|
||||
|
||||
@handler.expects(:reload!).never
|
||||
@handler.expects(:close_connection).with(request).once
|
||||
@handler.expects(:exit).never
|
||||
|
||||
@handler.process!
|
||||
assert_nil @handler.when_ready
|
||||
end
|
||||
|
||||
def test_restart_via_USR2_when_in_request
|
||||
request = mock
|
||||
FCGI.expects(:each).once.yields(request)
|
||||
@handler.expects(:signal).times(2).returns('USR2')
|
||||
@handler.expects(:exit_handler).never
|
||||
|
||||
@handler.expects(:reload!).never
|
||||
@handler.expects(:close_connection).with(request).once
|
||||
@handler.expects(:exit).never
|
||||
@handler.expects(:restart!).once
|
||||
|
||||
@handler.process!
|
||||
assert_equal :restart, @handler.when_ready
|
||||
end
|
||||
|
||||
def test_interrupted_via_TERM
|
||||
request = mock
|
||||
FCGI.expects(:each).once.yields(request)
|
||||
::Rack::Handler::FastCGI.expects(:serve).once.returns('TERM')
|
||||
|
||||
@handler.expects(:reload!).never
|
||||
@handler.expects(:close_connection).never
|
||||
|
||||
@handler.process!
|
||||
assert_nil @handler.when_ready
|
||||
end
|
||||
|
||||
def test_runtime_exception_in_fcgi
|
||||
error = RuntimeError.new('foo')
|
||||
FCGI.expects(:each).times(2).raises(error)
|
||||
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^retrying/))
|
||||
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/))
|
||||
@handler.process!
|
||||
end
|
||||
|
||||
def test_runtime_error_in_dispatcher
|
||||
request = mock
|
||||
error = RuntimeError.new('foo')
|
||||
FCGI.expects(:each).once.yields(request)
|
||||
::Rack::Handler::FastCGI.expects(:serve).once.raises(error)
|
||||
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^unhandled/))
|
||||
@handler.process!
|
||||
end
|
||||
|
||||
def test_signal_exception_in_fcgi
|
||||
error = SignalException.new('USR2')
|
||||
FCGI.expects(:each).once.raises(error)
|
||||
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/))
|
||||
@handler.process!
|
||||
end
|
||||
|
||||
def test_signal_exception_in_dispatcher
|
||||
request = mock
|
||||
error = SignalException.new('USR2')
|
||||
FCGI.expects(:each).once.yields(request)
|
||||
::Rack::Handler::FastCGI.expects(:serve).once.raises(error)
|
||||
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/))
|
||||
@handler.process!
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class RailsFCGIHandlerPeriodicGCTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@log = StringIO.new
|
||||
end
|
||||
|
||||
def teardown
|
||||
GC.enable
|
||||
end
|
||||
|
||||
def test_normal_gc
|
||||
@handler = RailsFCGIHandler.new(@log)
|
||||
assert_nil @handler.gc_request_period
|
||||
|
||||
# When GC is enabled, GC.disable disables and returns false.
|
||||
assert_equal false, GC.disable
|
||||
end
|
||||
|
||||
def test_periodic_gc
|
||||
@handler = RailsFCGIHandler.new(@log, 10)
|
||||
assert_equal 10, @handler.gc_request_period
|
||||
|
||||
request = mock
|
||||
FCGI.expects(:each).times(10).yields(request)
|
||||
|
||||
@handler.expects(:run_gc!).never
|
||||
9.times { @handler.process! }
|
||||
@handler.expects(:run_gc!).once
|
||||
@handler.process!
|
||||
|
||||
assert_nil @handler.when_ready
|
||||
end
|
||||
end
|
||||
end # uses_gem "fcgi"
|
|
@ -53,18 +53,6 @@ class AppGeneratorTest < GeneratorsTestCase
|
|||
assert_match /Invalid value for \-\-database option/, content
|
||||
end
|
||||
|
||||
def test_dispatchers_are_not_added_by_default
|
||||
run_generator
|
||||
assert_no_file "public/dispatch.cgi"
|
||||
assert_no_file "public/dispatch.fcgi"
|
||||
end
|
||||
|
||||
def test_dispatchers_are_added_if_required
|
||||
run_generator ["--with-dispatchers"]
|
||||
assert_file "public/dispatch.cgi"
|
||||
assert_file "public/dispatch.fcgi"
|
||||
end
|
||||
|
||||
def test_config_database_is_added_by_default
|
||||
run_generator
|
||||
assert_file "config/database.yml", /sqlite3/
|
||||
|
|
|
@ -30,6 +30,10 @@ module TestHelpers
|
|||
def app_path(*args)
|
||||
tmp_path(*%w[app] + args)
|
||||
end
|
||||
|
||||
def rails_root
|
||||
app_path
|
||||
end
|
||||
end
|
||||
|
||||
module Rack
|
||||
|
|
Loading…
Reference in a new issue