Assume I have a Clojurescript namespace called main with a atom within it called state.
(ns main)
(atom state nil)
I compile my Clojurescript app, run it on a server, fire up the Clojurescript repl and then connect to my server using a browser. Everything works dandy.
In the Clojurescript repl, I can comfirm the usual
> (+ 1 1)
2
> (js/alert "Hey there") //shows an alert dialog with "Hey there" in the browser
nil
> main.state
(atom nil)
The Clojurescript repl is great for development. So, clearly I can get and (using swap! or reset!) set the value of a Clojurescript atom from a Clojure application. I was wondering if there was a way to have a connection between the value of an atom in my Clojurescript project and a running Clojure application. Perhaps the Clojurescript client connects to a specified port and sends the result to some Clojure server waiting on that port. Simply stated, I was wondering if it might be possible to have a running server application share the value of the state client atom.
Why, you might ask? Well, I was thinking it would be nice to write the value of the state atom to a actual file (state.clj) whenever state is modified in the running Clojurescript application. This way, I could always have a view of the current value of state. I could use something like emacs (global-auto-revert-mode t) to make sure that state.clj buffer was always fairly recent. This is a little like having a debugger.
Beyond that, my real desire is to then make it so that the running Clojure application would also periodically poll state.clj itself. When the server detects that I modified state.clj, it would accept the modification as the new value of the Clojurescript state atom. It would then do something like what the Clojurescript repl does, illustrated in the following pseudo code:
(send-to-client-for-evaluation
(compile-into-js
(reset!
main.state
the-read-string-value-of-the-content-of-state.clj)))
Basically, I want the ability for the server to have something akin to a shared atom between the client and the server. I want the value of state to be bidirectionally shared between the client and the server. Is any of this possible, or am I just dreaming?
Sure, just make requests to a rest API sending data in EDN format, and voila! If you want to avoid polling, consider running your Jetty or Tomcat behind an Nginx instance to make use of the Nginx HTTP Push Module. I have a somewhat cobwebby example here (this was pre-ClojureScript, so I wrote the client in plain JS). And there is also gifsockets.
Related
Let's say I have a trivial boot-clj task that uses '[pandeiro.boot-http :refer [serve]]. If I make changes and need to restart the task it would complain that the port is already in use. Is there a way to kill jetty server without having to restart repl?
If you want only this many people use this pattern:
(defonce running-server (atom nil))
(defn stop-server [] (#running-server))
(defn go []
(reset! running-server
(org.httpkit.server/run-server
... your stuff here ...))
If this is a longer term project starting with something like the component framework solves this problem in a much more elegant way and allows for many dependent components (hence the name). This is a good place to get started and worth reading before starting your next project.
pandeiro.boot-http supports reloading of your handler so you don't have to restart the serve task. You need to configure it with :reload set to true:
On command line:
boot serve -H myapp.server/app -R wait
Or in your boot script:
(boot (serve :handler 'myapp.server/app :reload true) (wait))
For regular static files serve always returns the current version from the disk so there is no need for reloading for them.
If you really need to restart the task I guess you might be affected by issue in pandeiro.boot-http and might need to contact its maintainer.
I have a clojure/ring web application which I want to test with clj-webdriver. Is there a simple way to run the ring webserver and then run the tests such that they target the ring instance?
I'm thinking of a usage something like:
$ lein with-ring test
I have two ideas so far:
write a custom higher order leiningen plugin. Seems too complicated. Does a similar plugin already exist?
write a Makefile rule which starts ring, runs lein test, find&kill the ring process - too complicated/hacky
Is there a simpler way?
If you're using clojure.test you can call use-fixtures to start/stop your server from within the tests, e.g.:
(use-fixtures
:once
(fn [f]
(let [server (ring.adapter.jetty/run-jetty
#'your-app
{:port 1234 :join? false})]
(try
(f)
(finally
(.stop server))))))
Just adjust start/stop logic to your preferred webserver (jetty in this example; http-kit would be very similar). If you replace :once with :each you'll even have a fresh server instance for each single test.
How can I set it up, so that I can use Light Table on the Mac, connected to a Ubuntu-hosted nREPL, and create a new program/project.clj? Can anybody help me to understand what my approach should be, and where I'm going wrong?
I have a Ubuntu server, in VirtualBox, along with Leiningen, Pedestal, and a pedestal 'helloworld' sample program, that displays in a browser. I just can't connect to the same REPL that runs the helloworld program. I don't have client-side access or control to the server-side object.
I want to do client-server Clojure development from my Mac host LT, creating server-side programs, that I can view in a REPL.
I don't find LT to be intuitive in this area, and the advice I've read only suggests that LT will connect to an existing project/REPL. I want to create new objects, from the client
Assuming you are talking mostly about the server side of things (don't know much yet about ClojureScript), here is how you would setup a new project and code interactively against a remote repl.
lein new project-name in your VM.
Setup your project.clj the way you want it. You must include dependencies!
Start the REPL in your VM and note the port number
Commit your code to version control (git).
Checkout the same codebase for editing in LightTable in Mac OSX.
Connect to the remote REPL.
Open core.clj or create a new namespace file and start hacking.
Evaluate your code snippets or the entire file.
There are a couple of potential problems I foresee with this. First is that whenever you add a dependency, you will need to commit the change to version control, synchronize your VM working copy, restart your REPL, and reconnect LightTable to the remote REPL.
Second, you may have problems as the project grows beyond a few namespaces. I believe if your namespace requires another namespace from your project, the remote REPL process will (I think) try to load it off it's local classpath. If the dependency is not there or has changed, I don't think LightTable is smart enough to send the required namespaces over the wire. Try it out and let us know what the actual behavior is.
What's the best way to push new code to a production ring server without restarting the whole JVM?
Currently I use wrap-reload in production, but this doesn't quite work for me because sometimes I want to run commands in the repl (doing database migrations for example) before ring starts handling requests with the new code. Also various blogs and tutorials out there say not to use wrap-reload in production, though I don't understand why not.
I have come up with the following solution, but I confess I don't have a deep understanding of what's going on under the hood. I was wondering if I could get a sanity check by someone who does. Does this technique seem reasonable?
The idea is to have a path (/admin/reload-clj) that causes all the clojure code to be reloaded.
(defonce ^:dynamic *jetty*)
(declare reload-clj)
(defn app [req]
...
(when (= (req :uri) "/admin/reload-clj") (reload-clj req))
...)
(defn start-jetty []
(let [j (run-jetty app {:port (http-port) :join? false :max-threads 16})]
(dosync (ref-set *jetty* j))
j))
(defn reload-clj [req]
(future
(log/info "Reloading clojure code...")
(require '(whrusrv admin main utils wdb) :reload-all)
(.stop #*jetty*)
(start-jetty)
(log/info "Clojure reload success!"))
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Reloading..."})
(defn -main [& args]
(start-jetty))
The code you have will work, though you should be aware that :reload-all only loads a namespace and that namespaces dependencies. It does not recursively load dependencies of those namespaces.
I should add that reloading in this way is strongly not reccomended in a production system.
Newly deployed code might have errors that aren't apparent until the system is restarted (e.g, they depend on a var that is still defined from the running system but whose declaration was removed). The system will work fine but then fail on restart.
Loading code can also have side effects which could screw up your production environment. Although its good style to avoid these, the only way to truly be sure something unexpected won't happen is to do a JVM restart.
The best way to do a zero-downtime deploy on the JVM is a rolling deploy using a load balancer.
I had an intranet web service in common lisp at a previous job, so I don't know if that qualifies as production. Also, being in CL my answer is neither ring nor clojure specific. Still, it's useful for reloading code.
What I did is to start a swank server (part of slime) on the production lisp instance and connect to it even remotely from my desktop. That way I could write new code, rewrite code, debug, reload, etc. Obviously you'll have to be extra carefull in a real production system.
You can do similarly in clojure, you even have alternatives to swank, like nrepl.
You'll have to take security into account as well and allow only local connections, or whatever works for you.
I used to be able to start a web server in compojure like this:
(run-server {:port 8080} "/*" (servlet my-app))
Does anyone know where this function has gone in the latest compojure? (0.6.2)
The docs say I'm supposed to run it from the command line and use some freaky auto-reloading thing, at which point I might as well be using python.
You're looking at some seriously out-dated documentation.
For jetty, use
(use 'ring.adapter.jetty)
(defn start-web []
(run-jetty (var my-site) {:port 8080 :join? false}))
Where my-site is your top-level handler function.
You can call that function anywhere, including from the REPL in SLIME. Recompiling/redefining my-site will work on a running server, so there's no need for auto-reloading if you're already using an interactive environment.
EDIT: compojure has been split into ring & clout, with compojure itself remaining as a small selection of higher-level abstractions on top. Most of the actual server stuff and design documentation is now in ring. See https://github.com/mmcgrana/ring/wiki