Simple slug generator
One of the often-used patterns in web development is generating a quasi-random string. These are great for creating ‘slugs’ as often seen in URL shorteners, & in this mobile-friendly age, passing around small URLs is obligatory due to the limitations of small screens & SMS.
In Erlang, we are going to write a small module called ‘slug.erl’ which can handle all of this. Our home-spun module will yield a quasi-random string of characters & with that, we can let our imagination run wild with what to do with the results.
Before we open an editor, let’s lay out a few of the goals for what our code should do:
- The API should be flexible in generating slugs of a given length;
- For UX purposes, we don’t want any slug to contain the zero or capital oh characters — they look the same on some screens after all;
- Our code should make a valiant effort to generate solid randomness.
With that small specification in place, we should be good to take a stab at it.
Our slug should incorporate a range of characters, and although there are a plethora of ways to accomplish this, there’s nothing wrong with being pedantic about it and just list them all out by hand.
With that concession made, here’s the first few lines of our new module, ‘slug.erl’ :
-module(slug). -export([new/1]). -define(CHARS, "ABCDEFGHIJKLMNPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz123456789").
If you take a close look, you’ll notice that our macro does not include our forbidden characters (by design, of course).
Moving along, we next need to implement
new/1. First off,
it will have to seed a random value; a set of integers which
gets stored via
rand:seed/n in the calling process’s ‘process
dictionary.’ We use the ‘crypto’ module to generate the randomness,
though this may be overkill for some.
To understand this, open an Erlang shell & perform the following:
1> process_info(self()). % Note the `dictionary' value 2> <<X:32, Y:32, Z:32>> = crypto:strong_rand_bytes(12). 3> rand:seed(X, Y, Z). 4> process_info(self()). % Notice the change?
N.B. Older installs of OTP will want to leverage
random module, instead of the newer
From there, there’s almost nothing left to code. Here’s ‘slug.erl’ in its entirety:
-module(slug). -export([new/1]). -define(CHARS, "ABCDEFGHIJKLMNPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz123456789"). new(N) -> <<X:32, Y:32, Z:32>> = crypto:strong_rand_bytes(12), rand:seed(X, Y, Z), Cs = list_to_tuple(?CHARS), CsLen = tuple_size(Cs), [ pick(CsLen, Cs) || _ <- lists:seq(1, N) ]. pick(K, Ts) -> C = rand:uniform(K), element(C, Ts).
Working backwards, our
pick/2 routine first
chooses a random value between
1 and the given
K. With that random index, it
looks up the apt value from tuple
new/1 routine, after the familiar seeding
is accomplished, we then cast our string literal
from a list
(more on that here) to a tuple, so we can benefit from
Finally, for given length
N, we iterate
times to get a random string of characters from
the list we have specified in our macro.
With that, we can efficiently generate random slugs for constructing identifiers for URLs, create quasi-unique IDs for database records, or what have you. Not bad for a mere dozen lines of code!