lib/vegas/runner.rb in vegas-0.1.2 vs lib/vegas/runner.rb in vegas-0.1.3

- old
+ new

@@ -4,116 +4,132 @@ if Vegas::WINDOWS begin require 'win32/process' rescue - puts "Sorry, in order to use Vegas on Windows you need the win32-process gem:\n gem install win32-process" + puts "Sorry, in order to use Vegas on Windows you need the win32-process gem:\n " + + "gem install win32-process" end end module Vegas class Runner - attr_reader :app, :app_name, :rack_handler, :port, :host, :options, :args - + attr_reader :app, :app_name, :filesystem_friendly_app_name, :quoted_app_name, + :rack_handler, :port, :host, :options, :args + ROOT_DIR = File.expand_path(File.join('~', '.vegas')) PORT = 5678 HOST = WINDOWS ? 'localhost' : '0.0.0.0' - + def initialize(app, app_name, set_options = {}, runtime_args = ARGV, &block) - # initialize - @app = app - @app_name = app_name - @options = set_options || {} - @runtime_args = runtime_args + @options = set_options || {} + self.class.logger.level = options[:debug] ? Logger::DEBUG : Logger::INFO - - @rack_handler = @app.respond_to?(:detect_rack_handler) ? - @app.send(:detect_rack_handler) : Rack::Handler.get('thin') + @app = app + @app_name = app_name + + @filesystem_friendly_app_name = @app_name.gsub(/\W+/, "_") + @quoted_app_name = "'#{app_name}'" + + @runtime_args = runtime_args + @rack_handler = setup_rack_handler + # load options from opt parser @args = define_options do |opts| if block_given? opts.separator '' - opts.separator "#{app_name} options:" + opts.separator "#{quoted_app_name} options:" yield(self, opts, app) end end - # Call before run if before_run is a Proc - if before_run = options.delete(:before_run) and - before_run.is_a?(Proc) + # Handle :before_run hook + if (before_run = options.delete(:before_run)).respond_to?(:call) before_run.call(self) end - # set app options + # Set app options @host = options[:host] || HOST - if @app.respond_to?(:set) - @app.set(options) - @app.set(:vegas, self) + + if app.respond_to?(:set) + app.set(options) + app.set(:vegas, self) end - # initialize app dir + + # Make sure app dir is setup FileUtils.mkdir_p(app_dir) - return if options[:start] === false + + return if options[:start] == false + # evaluate the launch_path - path = if options[:launch_path] && options[:launch_path].respond_to?(:call) + path = if options[:launch_path].respond_to?(:call) options[:launch_path].call(self) else options[:launch_path] end + start(path) end def app_dir - File.join(ROOT_DIR, app_name) + File.join(ROOT_DIR, filesystem_friendly_app_name) end def pid_file - File.join(app_dir, "#{app_name}.pid") + File.join(app_dir, "#{filesystem_friendly_app_name}.pid") end def url_file - File.join(app_dir, "#{app_name}.url") + File.join(app_dir, "#{filesystem_friendly_app_name}.url") end def url "http://#{host}:#{port}" end def log_file - File.join(app_dir, "#{app_name}.log") + File.join(app_dir, "#{filesystem_friendly_app_name}.log") end def start(path = nil) logger.info "Running with Windows Settings" if WINDOWS - logger.info "Starting #{app_name}" + logger.info "Starting #{quoted_app_name}" begin check_for_running(path) find_port write_url launch!(url, path) - daemonize! unless options[:foreground] + daemonize! unless options[:foreground] run! rescue RuntimeError => e - logger.warn "There was an error starting #{app_name}: #{e}" + logger.warn "There was an error starting #{quoted_app_name}: #{e}" exit end end def find_port if @port = options[:port] - if !port_open? + announce_port_attempted + + unless port_open? logger.warn "Port #{port} is already in use. Please try another or don't use -P, for auto-port" end else @port = PORT - logger.info "Trying to start #{app_name} on Port #{port}" - while !port_open? + announce_port_attempted + + until port_open? @port += 1 - logger.info "Trying to start #{app_name} on Port #{port}" + announce_port_attempted end end end + + def announce_port_attempted + logger.info "Trying to start #{quoted_app_name} on port #{port}" + end def port_open?(check_url = nil) begin open(check_url || url) false @@ -128,23 +144,25 @@ def check_for_running(path = nil) if File.exists?(pid_file) && File.exists?(url_file) running_url = File.read(url_file) if !port_open?(running_url) - logger.warn "#{app_name} is already running at #{running_url}" + logger.warn "#{quoted_app_name} is already running at #{running_url}" launch!(running_url, path) exit! end end end def run! + logger.info "Running with Rack handler: #{@rack_handler.inspect}" + rack_handler.run app, :Host => host, :Port => port do |server| trap(kill_command) do ## Use thins' hard #stop! if available, otherwise just #stop server.respond_to?(:stop!) ? server.stop! : server.stop - logger.info "#{app_name} received INT ... stopping" + logger.info "#{quoted_app_name} received INT ... stopping" delete_pid! end end end @@ -183,15 +201,15 @@ logger.warn "pid not found at #{pid_file} : #{e}" end def status if File.exists?(pid_file) - logger.info "#{app_name} running" + logger.info "#{quoted_app_name} running" logger.info "PID #{File.read(pid_file)}" logger.info "URL #{File.read(url_file)}" if File.exists?(url_file) else - logger.info "#{app_name} not running!" + logger.info "#{quoted_app_name} not running!" end end # Loads a config file at config_path and evals it in the context of the @app. def load_config_file(config_path) @@ -199,15 +217,15 @@ config = File.read(config_path) # trim off anything after __END__ config.sub!(/^__END__\n.*/, '') @app.module_eval(config) end - + def self.logger=(logger) @logger = logger end - + def self.logger @logger ||= LOGGER if defined?(LOGGER) if !@logger @logger = Logger.new(STDOUT) @logger.formatter = Proc.new {|s, t, n, msg| "[#{t}] #{msg}\n"} @@ -217,15 +235,49 @@ end def logger self.class.logger end - - private + + private + def setup_rack_handler + # First try to set Rack handler via a special hook we honor + @rack_handler = if @app.respond_to?(:detect_rack_handler) + @app.detect_rack_handler + + # If they aren't using our hook, try to use their @app.server settings + elsif @app.respond_to?(:server) and @app.server + # If :server isn't set, it returns an array of possibilities, + # sorted from most to least preferable. + if @app.server.is_a?(Array) + handler = nil + @app.server.each do |server| + begin + handler = Rack::Handler.get(server) + break + rescue NameError => e + next + end + end + handler + + # :server might be set explicitly to a single option like "mongrel" + else + Rack::Handler.get(@app.server) + end + + # If all else fails, we'll use Thin + else + Rack::Handler::Thin + end + end + def define_options OptionParser.new("", 24, ' ') do |opts| - opts.banner = "Usage: #{app_name} [options]" + # TODO instead of app_name, we should determine the name of the script + # used to invoke Vegas and use that here + opts.banner = "Usage: your_executable_name [options]" opts.separator "" opts.separator "Vegas options:" opts.on("-s", "--server SERVER", "serve using SERVER (thin/mongrel/webrick)") { |s| @@ -276,10 +328,10 @@ exit end opts.on_tail("--version", "Show version") do if app.respond_to?(:version) - puts "#{app_name} #{app.version}" + puts "#{quoted_app_name} #{app.version}" end puts "rack #{Rack::VERSION.join('.')}" puts "sinatra #{Sinatra::VERSION}" if defined?(Sinatra) puts "vegas #{Vegas::VERSION}" exit