#!/usr/bin/env ruby require "find" require "webrick" require "open3" require "optparse" class ServeIt VERSION = [0, 0, 2] def self.main serve_dir, ignored_paths, command = parse_opts serve_dir = File.expand_path(serve_dir) Server.new(serve_dir, ignored_paths, command).serve end def self.parse_opts options = {:serve_dir => ".", :ignored_paths => []} parser = OptionParser.new do |opts| opts.banner = "Usage: #{$PROGRAM_NAME} [options] command" opts.on_tail("-s", "--serve-dir DIR", "Root directory for server") do |dir| options[:serve_dir] = dir end opts.on_tail("-i", "--ignore PATH", "Ignore changes to file or directory") do |path| options[:ignored_paths] << path end opts.on_tail("--version", "Show version") do |dir| puts ServeIt::VERSION.join('.') exit end end begin parser.parse!(ARGV) rescue OptionParser::InvalidOption => e $stderr.puts e $stderr.puts parser exit 1 end if ARGV.count == 0 command = nil elsif ARGV.count == 1 command = ARGV.fetch(0) else $stderr.write parser.to_s exit 1 end [options.fetch(:serve_dir), options.fetch(:ignored_paths), command] end class Server def initialize(serve_dir, ignored_paths, command) @mutex = Mutex.new @serve_dir = serve_dir @command = command @rebuilder = Rebuilder.new(@command, ignored_paths) if @command end def serve port = 8000 puts "Starting server at http://localhost:#{port}" server = WEBrick::HTTPServer.new(:Port => port) server.mount_proc '/' do |req, res| relative_path = req.path.sub(/^\//, '') local_abs_path = File.absolute_path(relative_path, @serve_dir) if relative_path == "favicon.ico" respond_to_favicon(res) else respond_to_path(res, relative_path, local_abs_path) end end trap 'INT' do server.shutdown end server.start end def respond_to_favicon(res) res.status = 404 end def respond_to_path(res, relative_path, local_abs_path) begin rebuild_if_needed rescue Rebuilder::RebuildFailed => e return respond_to_error(res, e.to_s) end if File.directory?(local_abs_path) respond_to_dir(res, relative_path, local_abs_path) else # We're building a file respond_to_file(res, local_abs_path) end end def respond_to_error(res, message) res.content_type = "text/html" res.body = "
" + message + "" end def respond_to_dir(res, rel_path, local_abs_path) res.content_type = "text/html" res.body = ( "