This is the code for the Functional Go Web Site, and is itself written in Functional Go. Client-side Functional Go generates JavaScript which powers the user interface, while server-side Functional Go runs on the server.
Feel free to use this project as a model for your own Functional Go web application, but be aware that in one respect it is not typical, it uses the Functional Go compiler to enable the interactive coding interface, something that is not needed in a typical web application.
Follow the [install instructions for Leiningen][lein] and go to the top level directory of this project:
To run locally on your laptop do the following from the command line:
lein do fgoc, cljsbuild once, ring server-headlessAnd then visit http://localhost:3000/
You can deploy to any server environment that supports standard WARs.
From the command line do:
lein do fgoc, cljsbuild once, ring uberwarwhich generates a file WAR file in the target directory, which you
can then deploy.
The client-code is a single-page app that uses AJAX to talk to a REST server.
The server component is src/clj/fgosite/server.go. It implements a
simple REST API:
/GET the single static HTML/CSS/JS page/:id/fgo/:filenamePUT the Functional Go code with this ID/:id/cljGET the Clojure code resulting from compiling the Functional Go with this ID/:id/evalGET the result of evaluating the Clojure code with this ID
The state is stored in the instance memory in a dictionary accessed via the Clojure transactional memory system. (This means it does not scale well to multiple servers -- See Improvements section below for how we might fix this.)
The main client code is src/cljs/fgosite.client.gos, and is an
example of seamlessly mixing Clojure and JavaScript technology. It
uses the Clojure Hiccups library to do client-side templating, and the
JavaScript JQuery library for everything else.
The styling is done using Boostrap.
The client code does the following:
- Uses a template to build HTML from Functional Go lists and dictionaries.
- Gets some sample code from the server and displays it, allowing the user to modify it.
- Using the MD5 hash of the code as an ID, gets the corresponding Clojure and evaluated results from the server.
- If the MD5 ID returned no result then upload the code to the server and retry step 3.
Currently if we scale to multiple load-balanced servers the client code will still work, but it may go through the retry loop multiple times until it eventually does a GET from one of the servers it previously did a PUT from. This would get worse as the number of servers increases.
A more scalable solution is to store the state in a Memcache server. We will still occasionally have to go around the retry loop because Memcache is not durable and items may be evicted from memory, but it should happen quite rarely.