diary at Telent Netowrks

Regaining my compojure#

Thu, 19 Feb 2015 11:16:22 +0000

Picking up $secret_project which I put down in November to do Sledge and Yablog, I find that the routing of http requests is a horrendous mess based on substring matching and ad hoc argument parsing, and would really benefit from some Compojure

(Now, that is, that I've actually got my head around how Compojure works)

Because I'm using Hiccup to compose pages out of parts, I thought it would be neat if I could return hiccup page bodies directly as responses so that they get uniformly topped and tailed and turned into response maps. Turns out to be fairly straightforward:

  1. define a type for the hiccup response
  2. extend the compojure.response/Renderable protocol to deal with the new type

(deftype Hiccupage [body])
(defn page-surround [page]
  (let [head [:head
              [:link {:rel "stylesheet" :type "text/css"
                      :href "/static/default.css"}]
              [:script {:src "/static/stuff.js"}]
              [:title "Hey you"]]]
    (into head (.body page))))
(extend-protocol compojure.response/Renderable
  (render [page req]
    {:status  200
     :headers {"Content-Type" "text/html; charset=utf-8"}
     :body   (hiccup.page/html5 (page-surround page))}))

Now we can return Hiccupage (hiccup page? geddit? I'm still looking for a better name, yes) objects directly from our routes

(defroutes app
    (GET "/hello/:name" [name] 
	  [[:h1 "hello"]
	   [:p "hello " name]
	   [:p "This is text. I like text"]]))

Yet another blog engine#

Mon, 16 Feb 2015 22:35:11 +0000

Yablog is the unimaginatively named "Yet Another Blog engine" and is what's now behind the blog at ww.telent.net - mostly because every third attempt to post with my-way ended up with having to relearn out how all the baroque git hooks worked.

It was supposed to be a two-hour Tuesday morning hack, but it's now almost the following Tuesday morning as I did not anticipate that

This entry is mostly just a placeholder to check the new posting process works, but do check out Instaparse if you haven't used it already.

The Invisible AUDIO element#

Thu, 22 Jan 2015 19:26:47 +0000

I said this morning that I was going to replace the browser-native audio controls with something which looks (approximately, at least) consistent everywhere. There's another couple of reasons for wanting to revisit the way we render the audio element

The nice thing about Om application state is that it's also a perfectly ordinary Clojure atom and we can call add-watch on it to have a perfectly ordinary Clojure(Script) function called whenever it changes. So what we're going to do is

Lucene, you don't do your Daddy's will#

Thu, 22 Jan 2015 07:10:54 +0000

I'm only showing you pictures of the new Sledge UI as a stopgap, as it will shortly be replaced with the new new Sledge UI.

I Am Keeping: the way we allow search refining. Each search you request (either by typing something in or by clicking an artist or title name) is turned into one of those little yellow label things and then you can remove it again just by clicking the 'x' (I stole this idiom from the amazon ec2 console ui, but it's also what the gmail composer does when you type addresses into the to/from lines).

I Am Losing: the link to 'view play queue'. Rather than a 'tabs' metaphor, we will have an 'expand' button in the player area at the bottom which causes the queue to roll up and overlay the search results.

Also To Go: the browser-native audio controls, which are getting replaced with something that is consistent across browsers.

But the bigger reason for writing: already gone, the Lucene interface for storing the music database. I started working on having Sledge monitor its music folders for additions/deletions, and realised just how hard it is to persuade Lucene to search for an exact match on a particular field with no ambiguity or helpful tokenising - which is necessary when you want to delete the record for a file that's not in the filesytsem any more. So now we're using a flat file and EDN and doing our own tokenising into a bunch of maps that are created at load time. It's actually faster too, but I don't think that's anything to do with Lucene, more with it being the second stab at the problem.

This has now taken substantially more than one weekend and it's still not done. Damn scope creep.

core.async with aleph#

Tue, 16 Dec 2014 07:34:29 +0000

(More sledge retrospective)

There was a point about three weeks ago when I thought I had a working audio player, then I tried using it on the phone and I got awkward screeches every thirty seconds through my stereo when I told it to play Ziggy Stardust. No, I'm not talking about David Bowie's voice here, this was genuine "a dog ate my CD" style digital audio corruption. The problem seemed to appear only on Wifi: I could replicate it on my laptop, but it didn't show up on localhost and it didn't show up over an ssh tunnel: I suspect it was something related to buffering/backpressure, and facing the prospect of debugging Java code with locks in it I punted and decided to try switching HTTP server instead.

Documentation on HTTP streaming from core.async channels with Aleph is kind of sparse, at least insofar as it is lacking a simple example of the kind of thing that should work. So here is my simple example of the kind of thing that worked for me: wrap the channel in a call to manifold.stream/->source and make sure that the things received on it are byte-array

(defn transcode-handler [request pathname]
  {:status 200
   :headers {"content-type" "audio/ogg"
             "x-hello" "goodbye"}
   :body (manifold/->source (transcode-chan pathname))})

(from server.clj )

I'm sure there are other things you could put on the channel that would also work, but I don't know what. java.nio.ByteBuffer doesn't seem to be one of them, but I'm only going on git commit history and a very fuzzy recollection of what I was doing that day, it might be that I did something else wrong.