Thanks for the list! I was being slightly facetious, but I’m happy to have been so easily proved wrong.
When I wrote that I was just coming from having looked at options for including an Etag header on resources served via Ring. What I discovered was that the semi-official middleware hasn’t been updated in 7 years and is still at version 0.1.0-SNAPSHOT. Now, I totally understand the “it’s finished” mentality for libraries, and it’s not like Etags are rocket science. It’s mildly annoying that the version remains 0.1.0-SNAPSHOT. I’d prefer, if a library is “done” that the author at least remove the “-SNAPSHOT” if not label it v1.0.0, but even that’s not that bad.
What really irked me is that
ring.middleware.etag only generates Etags for
File objects, while Compojure’s
route/resources returns static assets as stream objects if your app has been packaged as a JAR (e.g. by running
lein uberjar). This is trivially fixable, if you know about multi-methods, by providing your own implementation for
ring.middleware.etag/calculate-etag, and indeed from looking around it seems that most everyone has either done this or simply implemented Etags on their own.
I’ll be the first to admit that I’m as much to blame as the next Clojurian, as I didn’t bother to send a pull request, fork
ring.middleware.etag, write a blog post, or anything else. If I were to offer an excuse, it’s that, as an experienced Clojure programmer, the amount of effort required to fix this on my own is an order of magnitude less than what would be required to fix it in such a way that contributes back to the community. That said, for a beginner this could potentially be a major road-block, especially considering that
ring.middleware.etag would appear to work in development (since
ring.util.response/resource-response will return a
File when serving from a local filesystem, as in
lein ring server) but break only in production (if deploying as a JAR, since
resource-response will then switch to providing a stream).
The one bit of hope I take from this experience is that this clearly seems to be the sort of thing that
clojure.core.spec is designed to catch (though you’d have to be running checks in production, or at least some sort of staging environment). On the flip-side, in a language like Haskell, where
resource-response would have to return a product type covering
calculate-etag would only have implementations covering
File, the compiler would immediately be able to identify the issue.