scripts/docserver.rb (98 lines of code) (raw):

#!/usr/bin/env ruby # frozen_string_literal: true # Copyright 2021 Twitter, Inc. # SPDX-License-Identifier: Apache-2.0 require 'rubygems' require 'listen' require 'optparse' require 'pathname' require 'webrick' class Watcher def initialize(watch_paths, &block) @watch_paths = Set.new @listen_paths = Set.new watch_paths.each do |watch_path| Pathname.glob(watch_path).each do |pathname| pathname = pathname.realpath @watch_paths.add(pathname.to_s) if pathname.directory? @listen_paths.add(pathname.to_s) else @listen_paths.add(pathname.dirname.to_s) end end end @block = block end def watch_paths @watch_paths.to_a end def start_non_blocking return if @listener options = { latency: 1.0, wait_for_delay: 1.0 } @listener = Listen.to(*@listen_paths.to_a, options) do |modified, added, removed| changed = modified + added + removed changed.each do |path| pathname = Pathname.new(path).realpath loop do if @watch_paths.include?(pathname.to_s) @block.call break end break if pathname.root? pathname = pathname.parent end end end @listener.start end def stop return unless @listener @listener.stop @listener = nil end end class LocalWebServer attr_reader :port, :documents_path def initialize(documents_path, port) @documents_path = documents_path @port = port end def start_blocking server = WEBrick::HTTPServer.new( Port: port, DocumentRoot: documents_path, AccessLog: [] ) trap(:INT) do server.shutdown end server.start end end options = { documents_path: Dir.pwd, port: 3000 } OptionParser.new do |opts| opts.banner = "Usage: #{$PROGRAM_NAME} [options] [watch path, ...]" opts.on('-d PATH', '--documents', 'Path to the documents.') do |value| options[:documents_path] = File.expand_path(value) end opts.on('-c COMMAND', '--command', 'A command to update the documents when watch path is modified') do |value| options[:update_command] = value end opts.on('-p PORT', '--port', Integer, 'A port to the web server listen.') do |value| options[:port] = value end end.parse! if options[:update_command] watcher = Watcher.new(ARGV) do puts 'Watched file change' system(options[:update_command]) end puts "Watching #{watcher.watch_paths.join(', ')} ..." watcher.start_non_blocking end local_web_server = LocalWebServer.new(options[:documents_path], options[:port]) puts "Running a web server at http://localhost:#{local_web_server.port} ..." local_web_server.start_blocking