Slugs Make Things Nice

Sometimes you'd rather look at a slug than a long hex string!

I have been slowly refining the blog as I go. The list of things that could be done is nearly endless. But since this is just a side project to help learn clojure, I can pick and choose what to work on. I decided it was time to replace the hex number in the url structure. As you might have guessed, that long hex string is the id field of the row in the database that represents each article. This is pretty handy and easy, so of course that's where I started with it.

But they don't make for very user friendly urls. I don't expect people to be typing in the urls for individual posts on my blog, but it's nice when the url can kind of indicate to a viewer where it leads. https://4d4ms.com/blog/abcd123 certainly doesn't do that. A common technique for this kind of situation is to "slugify" the title of the post. This involves taking some part of the title and allowing that to be the url. So how easy is that to do in clojure?

(defn slugify
  "Make a slug from a title with an option limit on the number of words"
  [title & {:keys [limit]}]
  (let [tokens (map #(str/lower-case %) (str/split title #"\s"))]
    (str/join "-" (take (or limit (count tokens)) tokens))))

Granted, it is a pretty naive approach since you could have lots of things in your title that don't make for valid urls, but with just 5 lines of code we were able to make things a little nicer.

mango.core> (require '[mango.util :refer [slugify]])
nil
mango.core> (slugify "Slugs Make Things Nice" :limit 5)
"slugs-make-things-nice"

If we wanted to do things more correctly we'd probably use someone elses code like slugger.

Want to unpack that 5 lines of code? Keep reading!

We're defining a function with defn named slugify. It takes at least one argument, title and optionally another argument denoted by the keyword :limit. We use the let form to refer to a sequence of tokens that we construct by first splitting the title at whitespaces using the regular expression #"\s". That returns a sequence of words1 which we execute str/lower-case on via map. On the last line we take the limit words, if limit is not nil otherwise we take all the words, and we str/join them back together with a hyphen in between them.

That's it for now. There are a couple of things we need to do to support looking up articles by slug or id on the back end, but I'll cover those in another post. 😴

  1. We're not accounting for multiple whitespace in a row! This and many other kinds of edge cases will be nice to cover when we get into writing tests for our code.