;; In an article which convinced many people to have a look at LISP ;; http://www.paulgraham.com/avg.html ;; Paul Graham wrote about how nice it was to write a web application from an ;; environment where everything was controllable from the REPL. ;; Here's how to create a web server at the REPL ;; First we'll need a function to generate a simple web page (defn yo [] "Hello") ;; Now we attach that to / (use 'compojure.core) (defroutes main-routes (GET "/" [] (yo))) ;; We should wrap our routing table in a handler (note the #') (use 'compojure.handler) (def app (site #'main-routes)) ;; And then we can run it with jetty (again note the #') (use 'ring.adapter.jetty) (def server (run-jetty #'app {:port 8080 :join? false})) ;; So now we're serving web pages. Go look at http://localhost:8080/ ;; The web browser of the gods: ;; $ watch -d -n 1 curl -sv http://localhost:8080/ ;; We can stop the server (.stop server) ;; And restart it: (.start server) ;; We can redefine those functions, and the change takes effect immediately: (defn yo [] "<h1>Hello<h1/>") ;; (you will need to refresh the page, unless you use twbotg) ;; HTML is a bit of a pain. We already have a syntax for tree-structured data: (use 'hiccup.page-helpers) (defn yo [] (html5 [:head [:title "Hello World"]] [:body [:h1 "Lisp is the future"]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; I'm confused: see http://stackoverflow.com/questions/5398569/can-clojure-be-made-dynamic ;; The reason for the #' above is to make sure that we can redefine main-routes ;; and app at runtime. Without the indirection, the changes don't seem to get picked up. ;; And yet they do get picked up when you redefine yo. ;; I do not understand what is going on here. Any explanation would be welcome!
To start a REPL with the necessary libraries available, you can use maven.
To install maven on ubuntu:
$ sudo apt-get install maven2
Put this xml in a file named pom.xml, and then type
$ mvn clojure:repl
in the same directory. For a swank server that you can use with emacs, use
$ mvn clojure:swank
The first time you run it it will do an incredible amount of downloading to get all the necessary bits, but after that it's fairly speedy.
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.aspden</groupId> <artifactId>hello-compojure</artifactId> <version>0.1-SNAPSHOT</version> <name>hello-compojure</name> <description>maven, clojure, emacs, swank, compojure: together at last</description> <url>http://www.learningclojure.com</url> <!-- repositories --> <repositories> <repository> <id>clojars</id> <url>http://clojars.org/repo/</url> </repository> <repository> <id>clojure</id> <url>http://build.clojure.org/releases</url> </repository> <repository> <id>central</id> <url>http://repo1.maven.org/maven2</url> </repository> </repositories> <!-- libraries --> <dependencies> <dependency> <groupId>compojure</groupId> <artifactId>compojure</artifactId> <version>0.6.2</version> </dependency> <dependency> <groupId>hiccup</groupId> <artifactId>hiccup</artifactId> <version>0.3.4</version> </dependency> <dependency> <groupId>ring</groupId> <artifactId>ring-jetty-adapter</artifactId> <version>0.3.7</version> </dependency> <dependency> <groupId>org.clojure</groupId> <artifactId>clojure</artifactId> <version>1.2.0</version> </dependency> <dependency> <groupId>org.clojure</groupId> <artifactId>clojure-contrib</artifactId> <version>1.2.0</version> </dependency> <dependency> <groupId>jline</groupId> <artifactId>jline</artifactId> <version>0.9.94</version> </dependency> <dependency> <groupId>swank-clojure</groupId> <artifactId>swank-clojure</artifactId> <version>1.2.1</version> </dependency> </dependencies> <build> <plugins> <!-- talios' clojure-maven-plugin provides mvn clojure:swank etc --> <plugin> <groupId>com.theoryinpractise</groupId> <artifactId>clojure-maven-plugin</artifactId> <version>1.3.2</version> </plugin> <!-- The versions plugin allows you to find out what you can upgrade --> <!-- mvn versions:help --> <!-- mvn versions:display-dependency-updates --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>versions-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
#'app is short for (var app). For example:
ReplyDelete(def foo 10)
(type (var foo))
(type #'foo)
Passing a var (instead of the value) transparently re-reads the value inside the var in `run-jetty`, which leads to the dynamic magic. I blogged about it here:
http://charsequence.blogspot.com/2010/09/interactive-web-development-with.html
Regards,
Shantanu
What magic is "twbotg"?
ReplyDeletetwbotg = the web browser of the gods =
ReplyDelete$ watch -d -n 1 curl -sv http://localhost:8080/
Ah - pure nectar.
ReplyDeleteVery helpful. Thanks for sharing.
ReplyDelete