#!/usr/bin/env ruby require 'optparse' env = ENV['RACK_ENV'] || 'development' browse = false server = nil public_dir = 'public' if File.directory?('public') options = {:Port => 9393, :Host => '127.0.0.1', :AccessLog => [], :Path => '/'} opts = OptionParser.new("", 24, ' ') { |opts| opts.banner = "Usage: shotgun [ruby options] [rack options] [rackup config]" opts.separator "" opts.separator "Ruby options:" lineno = 1 opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line| eval line, TOPLEVEL_BINDING, "-e", lineno lineno += 1 } opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") { $DEBUG = true } opts.on("-w", "--warn", "turn warnings on for your script") { $-w = true } opts.on("-I", "--include PATH", "specify $LOAD_PATH (may be used more than once)") { |path| $LOAD_PATH.unshift(*path.split(":")) } opts.on("-r", "--require LIBRARY", "require the library, before executing your script") { |library| require library } opts.separator "" opts.separator "Rack options:" opts.on("-s", "--server SERVER", "server (webrick, mongrel, thin, etc.)") { |s| server = s } opts.on("-o", "--host HOST", "listen on HOST (default: 127.0.0.1)") { |host| options[:Host] = host } opts.on("-p", "--port PORT", "use PORT (default: 9393)") { |port| options[:Port] = port } opts.on("-E", "--env ENVIRONMENT", "use ENVIRONMENT for defaults (default: development)") { |e| env = e } opts.separator "" opts.separator "Shotgun options:" opts.on("-O", "--browse", "open browser immediately after starting") { |browse| browse = true } opts.on("-u", "--url URL", "specify url path (default: /)") { |url| options[:Path] = url } opts.on("-P", "--public PATH", "serve static files under PATH") { |path| public_dir = path } opts.on_tail("-h", "--help", "show this message") do puts opts exit end opts.on_tail("--version", "show version") do require 'rack' puts "Rack #{Rack.version}" exit end opts.parse! ARGV } config = ARGV[0] || "config.ru" abort "configuration #{config} not found" unless File.exist? config # extract additional arguments from first #\ line in config file. if File.read(config)[/^#\\(.*)/] opts.parse! $1.split(/\s+/) end # use the BROWSER environment variable or fall back to a more or less standard # set of commands ENV['BROWSER'] ||= %w[open xdg-open x-www-browser firefox opera mozilla netscape].find do |comm| next if comm == 'open' && `uname` !~ /Darwin/ ENV['PATH'].split(':').any? { |dir| File.executable?("#{dir}/#{comm}") } end ENV['RACK_ENV'] = env require 'rack' require 'rack/utils' require 'rack/request' require 'rack/response' require 'rack/lint' require 'rack/commonlogger' require 'rack/showexceptions' require 'shotgun' require 'thin' if server.to_s.downcase == 'thin' server = Rack::Handler.get(server) || Rack::Handler.default app = Rack::Builder.new do # these middleware run in the master process. use Shotgun::Static, public_dir if public_dir use Shotgun::SkipFavicon # loader forks the child and runs the embedded config followed by the # application config. run Shotgun::Loader.new(config) { case env when 'development' use Rack::CommonLogger, STDERR unless server.name =~ /CGI/ use Rack::ShowExceptions use Rack::Lint when 'deployment', 'production' use Rack::CommonLogger, STDERR unless server.name =~ /CGI/ end } end Shotgun.enable_copy_on_write # trap exit signals downward = false ['INT', 'TERM', 'QUIT'].each do |signal| trap(signal) do exit! if downward downward = true server.shutdown if server.respond_to?(:shutdown) Process.wait rescue nil exit! end end # load shotgun.rb in current working directory if it exists Shotgun.preload base_url = "http://#{options[:Host]}:#{options[:Port]}#{options[:Path]}" puts "== Shotgun/#{server.to_s.sub(/Rack::Handler::/, '')} on #{base_url}" server.run app, options do |inst| if browse if ENV['BROWSER'] system "#{ENV['BROWSER']} '#{base_url}'" else abort "BROWSER environment variable not set and no browser detected" end end end