use(require(“atomy”))

require(“webrick”) require(“puma”)

– shim in svg content-type support WEBrick HTTPUtils DefaultMimeTypes = “image/svg+xml”

class(Server):

def(initialize(@source, @destination, @processor, @renderer)):
  @mutex = Mutex new

def(Server serve(port)):

server = Puma Server new(
  [env]:
    relative-path = env["REQUEST_PATH"] sub(r"^\/", "")
    local-abs-path = File absolute-path(relative-path, @destination)

    if(relative-path == "favicon.ico")
      then: respond-to-favicon
      else: respond-to-path(relative-path, local-abs-path)
)

server add-tcp-listener("0.0.0.0", port)

-- server mount-proc("/") [req, res]:

trap("INT"):
  server stop

puts(i"serving on port #{port}")
server run(false)

def(Server respond-to-favicon()):

[404, #{}, []]

def(Server respond-to-path(relative-path, local-abs-path)):

do {
  rebuild-if-needed

  if(File directory?(local-abs-path))
    then: respond-to-file(File join(local-abs-path, "index.html"))
    else: respond-to-file(local-abs-path)
} rescue:
  e:
    backtrace = Rubinius Backtrace backtrace(e locations) show("\n", false)
    message = i"#{e to-s}\n#{backtrace}"
    puts(message)
    respond-with-error(message)

def(Server respond-with-error(message)):

[500, #{"Content-Type" -> "text/html"}, [i"<pre>#{message}</pre>"]]

def(Server respond-to-file(local-abs-path)):

[200, #{"Content-Type" -> guess-content-type(local-abs-path)}, [File read(local-abs-path)]]

def(Server guess-content-type(path)):

extension = File extname(path) sub(r"^\.", "")
WEBrick HTTPUtils DefaultMimeTypes fetch(extension):
  "application/octet-stream"

def(Server rebuild-if-needed):

@mutex synchronize:
  when(@last-disk-state == disk-state):
    return

  puts(i"rebuilding #{@source}...")

  -- clear out already-loaded document modules
  $LOADED_FEATURES reject! &.end-with?(".any")

  @processor process(@source, @renderer, @destination)

  puts("done")

  -- Save disk state after rebuilding, to ignore the built artifacts
  -- themselves
  @last-disk-state = disk-state

– TODO: if this is defined in the class: we get an unknown constant 'Particle' def(Server disk-state):

-- collect disk state of files surrounding input file
Dir glob(File expand-path("../**/*", @source)) collect &.(
  File absolute-path(_)
) collect [path] {
  [path, File stat(path) mtime to-s]
} sort