lib/mongrel/rails.rb in mongrel-0.3.11 vs lib/mongrel/rails.rb in mongrel-0.3.12

- old
+ new

@@ -1,85 +1,178 @@ require 'mongrel' require 'cgi' -# Implements a handler that can run Rails and serve files out of the -# Rails application's public directory. This lets you run your Rails -# application with Mongrel during development and testing, then use it -# also in production behind a server that's better at serving the -# static files. -# -# The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype -# mapping that it should add to the list of valid mime types. -# -# It also supports page caching directly and will try to resolve a request -# in the following order: -# -# * If the requested exact PATH_INFO exists as a file then serve it. -# * If it exists at PATH_INFO+".html" exists then serve that. -# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go. -# -# This means that if you are using page caching it will actually work with Mongrel -# and you should see a decent speed boost (but not as fast as if you use lighttpd). -# -# An additional feature you can use is -class RailsHandler < Mongrel::HttpHandler - attr_reader :files - attr_reader :guard - - def initialize(dir, mime_map = {}) - @files = Mongrel::DirHandler.new(dir,false) - @guard = Mutex.new +module Mongrel + module Rails - # register the requested mime types - mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) } - end - - # Attempts to resolve the request as follows: - # - # - # * If the requested exact PATH_INFO exists as a file then serve it. - # * If it exists at PATH_INFO+".html" exists then serve that. - # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go. - def process(request, response) - return if response.socket.closed? - path_info = request.params[Mongrel::Const::PATH_INFO] - page_cached = request.params[Mongrel::Const::PATH_INFO] + ".html" - - if @files.can_serve(path_info) - # File exists as-is so serve it up - @files.process(request,response) - elsif @files.can_serve(page_cached) - # possible cached page, serve it up - request.params[Mongrel::Const::PATH_INFO] = page_cached - @files.process(request,response) - else - begin - cgi = Mongrel::CGIWrapper.new(request, response) - cgi.handler = self - + # Implements a handler that can run Rails and serve files out of the + # Rails application's public directory. This lets you run your Rails + # application with Mongrel during development and testing, then use it + # also in production behind a server that's better at serving the + # static files. + # + # The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype + # mapping that it should add to the list of valid mime types. + # + # It also supports page caching directly and will try to resolve a request + # in the following order: + # + # * If the requested exact PATH_INFO exists as a file then serve it. + # * If it exists at PATH_INFO+".html" exists then serve that. + # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go. + # + # This means that if you are using page caching it will actually work with Mongrel + # and you should see a decent speed boost (but not as fast as if you use lighttpd). + # + # An additional feature you can use is + class RailsHandler < Mongrel::HttpHandler + attr_reader :files + attr_reader :guard + + def initialize(dir, mime_map = {}) + @files = Mongrel::DirHandler.new(dir,false) + @guard = Mutex.new + + # register the requested mime types + mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) } + end + + # Attempts to resolve the request as follows: + # + # + # * If the requested exact PATH_INFO exists as a file then serve it. + # * If it exists at PATH_INFO+".html" exists then serve that. + # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go. + def process(request, response) + return if response.socket.closed? + + path_info = request.params[Mongrel::Const::PATH_INFO] + page_cached = request.params[Mongrel::Const::PATH_INFO] + ".html" + + if @files.can_serve(path_info) + # File exists as-is so serve it up + @files.process(request,response) + elsif @files.can_serve(page_cached) + # possible cached page, serve it up + request.params[Mongrel::Const::PATH_INFO] = page_cached + @files.process(request,response) + else + begin + cgi = Mongrel::CGIWrapper.new(request, response) + cgi.handler = self + + @guard.synchronize do + # Rails is not thread safe so must be run entirely within synchronize + Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body) + end + + # This finalizes the output using the proper HttpResponse way + cgi.out {""} + rescue Errno::EPIPE + # ignored + rescue Object => rails_error + STDERR.puts "Error calling Dispatcher.dispatch #{rails_error.inspect}" + STDERR.puts rails_error.backtrace.join("\n") + end + end + end + + + # Does the internal reload for Rails. It might work for most cases, but + # sometimes you get exceptions. In that case just do a real restart. + def reload! @guard.synchronize do - # Rails is not thread safe so must be run entirely within synchronize - Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body) + $".replace $orig_dollar_quote + GC.start + Dispatcher.reset_application! + ActionController::Routing::Routes.reload end - - # This finalizes the output using the proper HttpResponse way - cgi.out {""} - rescue Errno::EPIPE - # ignored - rescue Object => rails_error - STDERR.puts "Error calling Dispatcher.dispatch #{rails_error.inspect}" - STDERR.puts rails_error.backtrace.join("\n") end end - end - - def reload! - @guard.synchronize do - $".replace $orig_dollar_quote - GC.start - Dispatcher.reset_application! - ActionController::Routing::Routes.reload + # Creates Rails specific configuration options for people to use + # instead of the base Configurator. + class RailsConfigurator < Mongrel::Configurator + + # Creates a single rails handler and returns it so you + # can add it to a uri. You can actually attach it to + # as many URIs as you want, but this returns the + # same RailsHandler for each call. + # + # Requires the following options: + # + # * :docroot => The public dir to serve from. + # * :environment => Rails environment to use. + # * :cwd => The change to working directory + # + # And understands the following optional settings: + # + # * :mime => A map of mime types. + # + # Because of how Rails is designed you can only have + # one installed per Ruby interpreter (talk to them + # about thread safety). Because of this the first + # time you call this function it does all the config + # needed to get your rails working. After that + # it returns the one handler you've configured. + # This lets you attach Rails to any URI (and mulitple) + # you want, but still protects you from threads destroying + # your handler. + def rails(options={}) + + return @rails_handler if @rails_handler + + ops = resolve_defaults(options) + + # fix up some defaults + ops[:environment] ||= "development" + ops[:docroot] ||= "public" + ops[:mime] ||= {} + + + $orig_dollar_quote = $".clone + ENV['RAILS_ENV'] = ops[:environment] + require "#{ops[:cwd]}/config/environment" + require 'dispatcher' + require 'mongrel/rails' + + @rails_handler = RailsHandler.new(ops[:docroot], ops[:mime]) + end + + + # Reloads rails. This isn't too reliable really, but + # should work for most minimal reload purposes. Only reliable + # way it so stop then start the process. + def reload! + if not @rails_handler + raise "Rails was not configured. Read the docs for RailsConfigurator." + end + + log "Reloading rails..." + @rails_handler.reload! + log "Done reloading rails." + + end + + # Takes the exact same configuration as Mongrel::Configurator (and actually calls that) + # but sets up the additional HUP handler to call reload!. + def setup_rails_signals(options={}) + ops = resolve_defaults(options) + + if RUBY_PLATFORM !~ /mswin/ + setup_signals(options) + + # rails reload + trap("HUP") { + log "HUP signal received." + reload! + } + + log "Rails signals registered. HUP => reload (without restart). It might not work well." + else + log "WARNING: Rails does not support signals on Win32." + end + end end end end