class Hawkins::Commands::LiveServe

Constants

COMMAND_OPTIONS
LIVERELOAD_PORT

Attributes

is_running[R]
mutex[R]
running_cond[R]

Public Class Methods

init_with_program(prog) click to toggle source
# File lib/hawkins/liveserve.rb, line 27
def init_with_program(prog)
  prog.command(:liveserve) do |cmd|
    cmd.description "Serve your site locally with LiveReload"
    cmd.syntax "liveserve [options]"
    cmd.alias :liveserver
    cmd.alias :l

    add_build_options(cmd)
    COMMAND_OPTIONS.each do |key, val|
      cmd.option(key, *val)
    end

    cmd.action do |_, opts|
      opts["reload_port"] ||= LIVERELOAD_PORT
      opts["serving"] = true
      opts["watch"] = true unless opts.key?("watch")
      start(opts)
    end
  end
end
process(opts) click to toggle source
# File lib/hawkins/liveserve.rb, line 57
def process(opts)
  opts = configuration_from_options(opts)
  destination = opts["destination"]
  setup(destination)

  @reload_reactor.start(opts)
  @server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") }

  @server.mount("#{opts['baseurl']}/__livereload",
    WEBrick::HTTPServlet::FileHandler, LIVERELOAD_DIR)
  @server.mount(opts["baseurl"], ReloadServlet, destination, file_handler_opts)

  Jekyll.logger.info "Server address:", server_address(@server, opts)
  launch_browser(@server, opts) if opts["open_url"]
  boot_or_detach(@server, opts)
end
shutdown() click to toggle source
# File lib/hawkins/liveserve.rb, line 74
def shutdown
  @server.shutdown if @is_running
end
start(opts) click to toggle source
# File lib/hawkins/liveserve.rb, line 48
def start(opts)
  config = opts["config"]
  @reload_reactor = nil
  register_reload_hooks(opts)
  Jekyll::Commands::Build.process(opts)
  opts["config"] = config
  LiveServe.process(opts)
end

Private Class Methods

boot_or_detach(server, opts) click to toggle source

Keep in our area with a thread or detach the server as requested by the user. This method determines what we do based on what you ask us to do.

# File lib/hawkins/liveserve.rb, line 205
def boot_or_detach(server, opts)
  if opts["detach"]
    pid = Process.fork do
      server.start
    end

    Process.detach(pid)
    Jekyll.logger.info "Server detached with pid '#{pid}'.", \
      "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server."
  else
    t = Thread.new { server.start }
    trap("INT") { server.shutdown }
    t.join
  end
end
create_error_page() click to toggle source
# File lib/hawkins/liveserve.rb, line 121
def create_error_page
  @header["Content-Type"] = "text/html; charset=UTF-8"
  @body = IO.read(File.join(@config[:DocumentRoot], "404.html"))
end
enable_logging(opts) click to toggle source

Make the stack verbose if the user requests it.

# File lib/hawkins/liveserve.rb, line 224
def enable_logging(opts)
  opts[:AccessLog] = []
  level = WEBrick::Log.const_get(opts[:JekyllOptions]["verbose"] ? :DEBUG : :WARN)
  opts[:Logger] = WEBrick::Log.new($stdout, level)
end
enable_ssl(opts) click to toggle source

Add SSL to the stack if the user triggers –enable-ssl and they provide both types of certificates commonly needed. Raise if they forget to add one of the certificates.

# File lib/hawkins/liveserve.rb, line 235
def enable_ssl(opts)
  jekyll_opts = opts[:JekyllOptions]
  return if !jekyll_opts['ssl_cert'] && !jekyll_opts['ssl_key']
  if !jekyll_opts['ssl_cert'] || !jekyll_opts['ssl_key']
    raise "--ssl-cert or --ssl-key missing."
  end

  Jekyll.logger.info("LiveReload:", "Serving over SSL/TLS.  If you are using a "\
    "certificate signed by an unknown CA, you will need to add an exception for both "\
    "#{jekyll_opts['host']}:#{jekyll_opts['port']} and "\
    "#{jekyll_opts['host']}:#{jekyll_opts['reload_port']}")

  require "openssl"
  require "webrick/https"
  source_key = Jekyll.sanitized_path(jekyll_opts['source'], jekyll_opts['ssl_key'])
  source_certificate = Jekyll.sanitized_path(jekyll_opts['source'], jekyll_opts['ssl_cert'])
  opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.read(source_certificate))
  opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(File.read(source_key))
  opts[:SSLEnable] = true
end
file_handler_opts() click to toggle source

Recreate NondisclosureName under utf-8 circumstance

# File lib/hawkins/liveserve.rb, line 159
def file_handler_opts
  WEBrick::Config::FileHandler.merge(
    :FancyIndexing     => true,
    :NondisclosureName => [
      '.ht*', '~*'
    ]
  )
end
launch_browser(server, opts) click to toggle source
# File lib/hawkins/liveserve.rb, line 188
def launch_browser(server, opts)
  command =
    if Jekyll::Utils::Platforms.windows?
      "start"
    elsif Jekyll::Utils::Platforms.osx?
      "open"
    else
      "xdg-open"
    end
  system command, server_address(server, opts)
end
mime_types() click to toggle source
# File lib/hawkins/liveserve.rb, line 297
def mime_types
  file = File.expand_path('../mime.types', File.dirname(__FILE__))
  WEBrick::HTTPUtils.load_mime_types(file)
end
register_reload_hooks(opts) click to toggle source
# File lib/hawkins/liveserve.rb, line 79
def register_reload_hooks(opts)
  require_relative "websockets"
  @reload_reactor = LiveReloadReactor.new

  Jekyll::Hooks.register(:site, :post_render) do |site|
    regenerator = Jekyll::Regenerator.new(site)
    @changed_pages = site.pages.select do |p|
      regenerator.regenerate?(p)
    end
  end

  # A note on ignoring files: LiveReload errs on the side of reloading when it
  # comes to the message it gets.  If, for example, a page is ignored but a CSS
  # file linked in the page isn't, the page will still be reloaded if the CSS
  # file is contained in the message sent to LiveReload.  Additionally, the
  # path matching is very loose so that a message to reload "/" will always
  # lead the page to reload since every page starts with "/".
  Jekyll::Hooks.register(:site, :post_write) do
    if @changed_pages && @reload_reactor && @reload_reactor.running?
      ignore, @changed_pages = @changed_pages.partition do |p|
        Array(opts["ignore"]).any? do |filter|
          File.fnmatch(filter, Jekyll.sanitized_path(p.relative_path))
        end
      end
      Jekyll.logger.debug "LiveReload:", "Ignoring #{ignore.map(&:relative_path)}"
      @reload_reactor.reload(@changed_pages)
    end
    @changed_pages = nil
  end
end
server_address(server, opts) click to toggle source
# File lib/hawkins/liveserve.rb, line 171
def server_address(server, opts)
  address = server.config[:BindAddress]
  baseurl = "#{opts['baseurl']}/" if opts["baseurl"]
  port = server.config[:Port]

  if opts['ssl_cert'] && opts['ssl_key']
    protocol = "https"
  else
    protocol = "http"
  end

  "#{protocol}://#{address}:#{port}#{baseurl}"
end
setup(destination) click to toggle source

Do a base pre-setup of WEBRick so that everything is in place when we get ready to party, checking for an setting up an error page and making sure our destination exists.

# File lib/hawkins/liveserve.rb, line 115
def setup(destination)
  require_relative "./servlet"

  FileUtils.mkdir_p(destination)
  if File.exist?(File.join(destination, "404.html"))
    WEBrick::HTTPResponse.class_eval do
      def create_error_page
        @header["Content-Type"] = "text/html; charset=UTF-8"
        @body = IO.read(File.join(@config[:DocumentRoot], "404.html"))
      end
    end
  end
end
start_callback(detached) click to toggle source
# File lib/hawkins/liveserve.rb, line 257
def start_callback(detached)
  unless detached
    proc do
      mutex.synchronize do
        unless @reload_reactor.nil?
          @reload_reactor.reactor_mutex.synchronize do
            unless EM.reactor_running?
              @reload_reactor.reactor_running_cond.wait(@reload_reactor.reactor_mutex)
            end
          end
        end
        @is_running = true
        Jekyll.logger.info("Server running...", "press ctrl-c to stop.")
        running_cond.signal
      end
    end
  end
end
stop_callback(detached) click to toggle source
# File lib/hawkins/liveserve.rb, line 277
def stop_callback(detached)
  unless detached
    proc do
      mutex.synchronize do
        unless @reload_reactor.nil?
          @reload_reactor.stop
          @reload_reactor.reactor_mutex.synchronize do
            if EM.reactor_running?
              @reload_reactor.reactor_running_cond.wait(@reload_reactor.reactor_mutex)
            end
          end
        end
        @is_running = false
        running_cond.signal
      end
    end
  end
end
webrick_opts(opts) click to toggle source
# File lib/hawkins/liveserve.rb, line 132
def webrick_opts(opts)
  opts = {
    :JekyllOptions      => opts,
    :DoNotReverseLookup => true,
    :MimeTypes          => mime_types,
    :DocumentRoot       => opts["destination"],
    :StartCallback      => start_callback(opts["detach"]),
    :StopCallback       => stop_callback(opts["detach"]),
    :BindAddress        => opts["host"],
    :Port               => opts["port"],
    :DirectoryIndex     => %w(
      index.htm
      index.html
      index.rhtml
      index.cgi
      index.xml
    ),
  }

  enable_ssl(opts)
  enable_logging(opts)
  opts
end