Last active
August 29, 2015 13:56
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
I'm also new to Elixir, so keep that in mind :)
In lines 6-9,
- why use a case instead of a simple if-else? Is case preferred over if for even for simple, 2 branch only code?
- why the
r when r === 0 ->
instead of just a0 ->
? - 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 :))
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
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 :)