summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Run/Que/Server.hs (renamed from Run/Que.hs)2
-rw-r--r--Run/Que/Website.hs45
-rwxr-xr-xRun/Que/client.py95
-rw-r--r--Run/Que/index.md68
-rw-r--r--Run/Que/style.css4
-rw-r--r--default.nix14
6 files changed, 221 insertions, 7 deletions
diff --git a/Run/Que.hs b/Run/Que/Server.hs
index cf9467b..1acbe60 100644
--- a/Run/Que.hs
+++ b/Run/Que/Server.hs
@@ -5,7 +5,7 @@
{- | Interprocess communication
-}
-module Run.Que
+module Run.Que.Server
( main
)
where
diff --git a/Run/Que/Website.hs b/Run/Que/Website.hs
new file mode 100644
index 0000000..1de6bca
--- /dev/null
+++ b/Run/Que/Website.hs
@@ -0,0 +1,45 @@
+-- | spawns a few processes that serve the que.run website
+module Run.Que.Website where
+
+import Prelude
+import System.Environment as Environment
+import System.FilePath ( (</>) )
+import qualified System.Process as Process
+
+main :: IO ()
+main = do
+ args <- Environment.getArgs
+ let [src, ns] = if length args == 2
+ then take 2 args
+ else if length args == 1
+ then args ++ ["/"]
+ else error "usage: que-website <srcdir> [namespace]"
+ homepage <- getHomepageHtml (src </> "style.css") (src </> "index.md")
+ client <- readFile $ src </> "client.py"
+ putStrLn $ "serving " ++ src ++ " at " ++ ns
+ loop ns homepage client
+
+loop :: String -> FilePath -> FilePath -> IO ()
+loop ns homepage client =
+ serve (ns </> "index.html") homepage
+ >> serve (ns </> "_client/python") client
+ >> loop ns homepage client
+
+getHomepageHtml :: String -> String -> IO String
+getHomepageHtml style index = Process.readProcess
+ "pandoc"
+ [ "--self-contained"
+ , "--css"
+ , style
+ , "-i"
+ , index
+ , "--from"
+ , "markdown"
+ , "--to"
+ , "html"
+ ]
+ []
+
+serve :: FilePath -> FilePath -> IO ()
+serve path file =
+ Process.callProcess "curl" ["https://que.run" ++ path, "-d", file]
diff --git a/Run/Que/client.py b/Run/Que/client.py
new file mode 100755
index 0000000..8058a05
--- /dev/null
+++ b/Run/Que/client.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+"""
+simple client for que.run
+"""
+
+import argparse
+import urllib.request as request
+import urllib.parse
+import time
+import subprocess
+import sys
+
+# set to something ridiculously high so we don't run into timeouts while polling
+# or waiting for a message
+MAX_TIMEOUT = 100000000
+
+
+def main(argv=None):
+ cli = argparse.ArgumentParser(description=__doc__)
+ cli.add_argument(
+ "--host", default="https://que.run", help="where que-server is running"
+ )
+ cli.add_argument(
+ "--poll", default=False, action="store_true", help="stream data from the que"
+ )
+ cli.add_argument(
+ "--then",
+ help="when polling, run this shell command after each response, replacing '\que' with the target and '\msg' with the body of the response",
+ )
+ cli.add_argument(
+ "--serve",
+ default=False,
+ action="store_true",
+ help="when posting to the que, do so continuously in a loop. this can be used for serving a webpage or other file continuously",
+ )
+ cli.add_argument(
+ "target", help="namespace and path of the que, like 'ns/path/subpath'"
+ )
+ cli.add_argument(
+ "infile",
+ nargs="?",
+ type=argparse.FileType("r"),
+ help="data to put on the que. Use '-' for stdin, otherwise should be a readable file",
+ )
+
+ if argv is None:
+ args = cli.parse_args()
+ else:
+ args = cli.parse_args(argv)
+
+ if args.infile:
+ # send input data
+ data = args.infile.read().encode("utf-8").strip()
+ if args.serve:
+ # loop until ^C
+ while not time.sleep(1):
+ with request.urlopen(
+ f"{args.host}/{args.target}", data=data, timeout=MAX_TIMEOUT
+ ) as req:
+ pass
+ else:
+ with request.urlopen(
+ f"{args.host}/{args.target}", data=data, timeout=MAX_TIMEOUT
+ ) as req:
+ pass
+ else:
+ # no input data, do a read instead
+ params = urllib.parse.urlencode({"poll": args.poll})
+ url = f"{args.host}/{args.target}?{params}"
+ with request.urlopen(url) as req:
+ if args.poll:
+ while not time.sleep(1):
+ msg = req.readline().decode("utf-8").strip()
+ print(msg)
+ if args.then:
+ subprocess.run(
+ args.then.replace("\msg", msg).replace("\que", args.target),
+ shell=True,
+ )
+ else:
+ msg = req.read().decode("utf-8").strip()
+ print(msg)
+ if args.then:
+ subprocess.run(
+ args.then.replace("\msg", msg).replace("\que", args.target),
+ shell=True,
+ )
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except KeyboardInterrupt:
+ print("Interrupted")
+ sys.exit(0)
diff --git a/Run/Que/index.md b/Run/Que/index.md
new file mode 100644
index 0000000..b619de7
--- /dev/null
+++ b/Run/Que/index.md
@@ -0,0 +1,68 @@
+% que.run
+
+que is the concurrent, async runtime in the cloud
+
+ - runtime concurrency anywhere you have a network connection
+ - multilanguage communicating sequential processes
+ - add Go-like channels to any language
+ - connect your microservices together with the simplest possible
+ plumbing
+ - async programming as easy as running two terminal commands
+
+HTTP routes on que.run are Golang-like channels with a namespace and a
+path. For example: `https://que.run/example/path/subpath`.
+
+## download the client
+
+There is a simple script `que` that acts as a client you can use to
+interact with the `que.run` service.
+
+Download it to somewhere on your `$PATH` and make it executable:
+
+ curl https://que.run/_client/python > ~/bin/que
+ chmod +x ~/bin/que
+ que --help
+
+The client requires a recent version of Python 3.
+
+## examples
+
+Here are some example applications, I will update these in the coming
+weeks with additional useful scripts.
+
+### desktop notifications
+
+Lets say we are running a job that takes a long time, maybe we are
+compiling or running a large test suite. Instead of watching the
+terminal until it completes, or flipping back to check on it every so
+often, we can create a listener that displays a popup notification when
+the job finishes.
+
+In one terminal run the listener:
+
+ que example/notify --then "notify-send '\que' '\msg'"
+
+In some other terminal run the job that takes forever:
+
+ runtests ; echo "tests are done" | que example/notify -
+
+When terminal 2 succeeds, terminal 1 will print "tests are done", then
+call the `notify-send` command, which displays a notification toast in
+Linux with title "`example/notify`" and content "`tests are done`".
+
+Que paths are multi-producer and multi-consumer, so you can add as many
+terminals as you want.
+
+On macOS you could use:
+
+ osascript -e 'display notification "\msg" with title "\que"'
+
+in place of notify-send.
+
+### ephemeral, serverless chat rooms
+
+coming soon...
+
+### collaborative jukebox
+
+coming soon...
diff --git a/Run/Que/style.css b/Run/Que/style.css
new file mode 100644
index 0000000..fa73fa4
--- /dev/null
+++ b/Run/Que/style.css
@@ -0,0 +1,4 @@
+/* perfect motherfucking css framework */
+body{max-width:650px;margin:40px auto;padding:0 10px;font:18px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";color:#444}h1,h2,h3{line-height:1.2}@media (prefers-color-scheme: dark){body{color:white;background:#444}a:link{color:#5bf}a:visited{color:#ccf}}
+
+/* my stuff */
diff --git a/default.nix b/default.nix
index 54db612..13ded48 100644
--- a/default.nix
+++ b/default.nix
@@ -49,7 +49,7 @@ in rec {
};
};
Com.Simatime.Serval = buildOS {
- deps = { que-server = Run.Que; };
+ deps = { que-server = Run.Que.Server; };
configuration = {
imports = [
./Com/Simatime/packages.nix
@@ -161,19 +161,21 @@ in rec {
];
};
};
- Run.Que = buildGhc {
- name = "Run.Que";
+ Run.Que.Server = buildGhc {
+ name = "Run.Que.Server";
nick = "que-server";
deps = [
- "aeson"
"async"
"protolude"
"scotty"
- "servant"
- "servant-server"
"stm"
"unagi-chan"
"unordered-containers"
];
};
+ Run.Que.Website = buildGhc {
+ name = "Run.Que.Website";
+ nick = "que-website";
+ deps = [ "process" ];
+ };
}