shotgun 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #!/usr/bin/env ruby
  2. require 'optparse'
  3. env = ENV['RACK_ENV'] || 'development'
  4. browse = false
  5. server = nil
  6. public_dir = 'public' if File.directory?('public')
  7. options = {:Port => 9393, :Host => '127.0.0.1', :AccessLog => [], :Path => '/'}
  8. opts = OptionParser.new("", 24, ' ') { |opts|
  9. opts.banner = "Usage: shotgun [ruby options] [rack options] [rackup config]"
  10. opts.separator ""
  11. opts.separator "Ruby options:"
  12. lineno = 1
  13. opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line|
  14. eval line, TOPLEVEL_BINDING, "-e", lineno
  15. lineno += 1
  16. }
  17. opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
  18. $DEBUG = true
  19. }
  20. opts.on("-w", "--warn", "turn warnings on for your script") {
  21. $-w = true
  22. }
  23. opts.on("-I", "--include PATH",
  24. "specify $LOAD_PATH (may be used more than once)") { |path|
  25. $LOAD_PATH.unshift(*path.split(":"))
  26. }
  27. opts.on("-r", "--require LIBRARY",
  28. "require the library, before executing your script") { |library|
  29. require library
  30. }
  31. opts.separator ""
  32. opts.separator "Rack options:"
  33. opts.on("-s", "--server SERVER", "server (webrick, mongrel, thin, etc.)") { |s|
  34. server = s
  35. }
  36. opts.on("-o", "--host HOST", "listen on HOST (default: 127.0.0.1)") { |host|
  37. options[:Host] = host
  38. }
  39. opts.on("-p", "--port PORT", "use PORT (default: 9393)") { |port|
  40. options[:Port] = port
  41. }
  42. opts.on("-E", "--env ENVIRONMENT", "use ENVIRONMENT for defaults (default: development)") { |e|
  43. env = e
  44. }
  45. opts.separator ""
  46. opts.separator "Shotgun options:"
  47. opts.on("-O", "--browse", "open browser immediately after starting") { |browse|
  48. browse = true
  49. }
  50. opts.on("-u", "--url URL", "specify url path (default: /)") { |url|
  51. options[:Path] = url
  52. }
  53. opts.on("-P", "--public PATH", "serve static files under PATH") { |path|
  54. public_dir = path
  55. }
  56. opts.on_tail("-h", "--help", "show this message") do
  57. puts opts
  58. exit
  59. end
  60. opts.on_tail("--version", "show version") do
  61. require 'rack'
  62. puts "Rack #{Rack.version}"
  63. exit
  64. end
  65. opts.parse! ARGV
  66. }
  67. config = ARGV[0] || "config.ru"
  68. abort "configuration #{config} not found" unless File.exist? config
  69. # extract additional arguments from first #\ line in config file.
  70. if File.read(config)[/^#\\(.*)/]
  71. opts.parse! $1.split(/\s+/)
  72. end
  73. # use the BROWSER environment variable or fall back to a more or less standard
  74. # set of commands
  75. ENV['BROWSER'] ||=
  76. %w[open xdg-open x-www-browser firefox opera mozilla netscape].find do |comm|
  77. next if comm == 'open' && `uname` !~ /Darwin/
  78. ENV['PATH'].split(':').any? { |dir| File.executable?("#{dir}/#{comm}") }
  79. end
  80. ENV['RACK_ENV'] = env
  81. require 'rack'
  82. require 'rack/utils'
  83. require 'rack/request'
  84. require 'rack/response'
  85. require 'rack/lint'
  86. require 'rack/commonlogger'
  87. require 'rack/showexceptions'
  88. require 'shotgun'
  89. require 'thin' if server.to_s.downcase == 'thin'
  90. server = Rack::Handler.get(server) || Rack::Handler.default
  91. app =
  92. Rack::Builder.new do
  93. # these middleware run in the master process.
  94. use Shotgun::Static, public_dir if public_dir
  95. use Shotgun::SkipFavicon
  96. # loader forks the child and runs the embedded config followed by the
  97. # application config.
  98. run Shotgun::Loader.new(config) {
  99. case env
  100. when 'development'
  101. use Rack::CommonLogger, STDERR unless server.name =~ /CGI/
  102. use Rack::ShowExceptions
  103. use Rack::Lint
  104. when 'deployment', 'production'
  105. use Rack::CommonLogger, STDERR unless server.name =~ /CGI/
  106. end
  107. }
  108. end
  109. Shotgun.enable_copy_on_write
  110. # trap exit signals
  111. downward = false
  112. ['INT', 'TERM', 'QUIT'].each do |signal|
  113. trap(signal) do
  114. exit! if downward
  115. downward = true
  116. server.shutdown if server.respond_to?(:shutdown)
  117. Process.wait rescue nil
  118. exit!
  119. end
  120. end
  121. # load shotgun.rb in current working directory if it exists
  122. Shotgun.preload
  123. base_url = "http://#{options[:Host]}:#{options[:Port]}#{options[:Path]}"
  124. puts "== Shotgun/#{server.to_s.sub(/Rack::Handler::/, '')} on #{base_url}"
  125. server.run app, options do |inst|
  126. if browse
  127. if ENV['BROWSER']
  128. system "#{ENV['BROWSER']} '#{base_url}'"
  129. else
  130. abort "BROWSER environment variable not set and no browser detected"
  131. end
  132. end
  133. end