Skip to content

Instantly share code, notes, and snippets.

@caindy
Last active August 29, 2015 13:56
Show Gist options
  • Save caindy/8821426 to your computer and use it in GitHub Desktop.
Save caindy/8821426 to your computer and use it in GitHub Desktop.
Elixir module to create unique identifiers of specified length, with serialization to and from strings. Put here to solicit feedback on coding style/correctness, as I am new to Elixir.
defmodule Hello.Util.UUID do
def new(byte_count // 16) do
dividend = div(byte_count, 16)
remainder = rem(byte_count, 16)
cnt = case remainder do
r when r === 0 -> dividend
_ -> dividend + remainder
end
make cnt, byte_count, <<>>
end
defp make(0, byte_count, acc), do:
binary_part(acc, 0, byte_count)
defp make(cnt, byte_count, acc) do
u = :ossp_uuid.make :v4, :binary
make(cnt - 1, byte_count, acc <> u)
end
def valid?(uuid, bytes // 16)
def valid?(uuid, bytes) when is_binary(uuid) do
case uuid do
<<_::[size(bytes), binary]>> -> true
_ -> false
end
end
def valid?(uuid, bytes) when is_list(uuid) do
chars = bytes * 2
{:ok, r} = Regex.compile("[A-F0-9]{#{chars}}")
Regex.match?(r, uuid) and length_valid?(uuid, chars)
end
defp length_valid?(uuid, chars) when is_binary(uuid), do:
length_valid?(bitstring_to_list(uuid), chars)
defp length_valid?(uuid, chars) when is_list(uuid), do:
length(uuid) === chars
def to_string(uuid) when is_binary(uuid) do
l = lc <<nibble::4>> inbits uuid, do: hd(integer_to_list(nibble, 16))
:erlang.list_to_binary l
end
def from_string(uuid) when is_binary(uuid), do:
from_string(bitstring_to_list(uuid))
def from_string(uuid) when is_list(uuid), do:
append_byte(uuid, <<>>)
defp append_byte([], acc), do: acc
defp append_byte([x,y|rest], acc), do:
append_byte(rest, acc <> <<list_to_integer([x, y], 16)>>)
end
@chrismccord
Copy link

Nice work!

My only suggestion would be to avoid single/short character variable names in favor of descriptive names to optimize for clarity. I also tend to only make , do: shorthand funcs on a single line, requiring do/end if things need to drop to another line (personal preference though, no right or wrong).

I would otherwise consider this highly clean, idiomatic Elixir :)

@zsoldosp
Copy link

zsoldosp commented Feb 5, 2014

I'm also new to Elixir, so keep that in mind :)

In lines 6-9,

  1. why use a case instead of a simple if-else? Is case preferred over if for even for simple, 2 branch only code?
  2. why the r when r === 0 -> instead of just a 0 ->?
  3. it seems to me that the case is equivalent to cnt = dividend + remainder. Or did I overlook something?

As said, I'm new, and I'm glad to have this learning opportunity to learn on someone else's code example (probably I should push something public myself, but felt too shy :))

@caindy
Copy link
Author

caindy commented Feb 5, 2014

Hi Peter, thanks for checking this out!

So, I've been using the if/unless/cond for nil tests, mostly, but I agree the case here obscures the intent. In fact, this code was wrong. Here's the corrected version.

  def new(byte_count // 16) do 
    dividend = div(byte_count, 16)
    add = if rem(byte_count, 16) > 0, do: 1, else: 0
    make (dividend + add), byte_count, <<>>
  end

Thanks for your feedback 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment