diff --git a/bin/serveit b/bin/serveit new file mode 100755 index 0000000..77f4f37 --- /dev/null +++ b/bin/serveit @@ -0,0 +1,203 @@ +#!/usr/bin/env ruby + +require "find" +require "webrick" +require "open3" +require "optparse" + +class ServeIt + VERSION = [0, 0, 1] + 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 = ( + "