Current section
Files
Jump to
Current section
Files
lib/anonymous_name_generator.ex
defmodule AnonymousNameGenerator do
@moduledoc """
This library will create Heroku-like names
It can create random names like "pungent-slime"
If you need more variation, you can generate names likes "pungent-slime-1234"
It can also create consistent names if you pass in integers
Example. This will always return `"interesting-emu"`
user = %User{id: 341, timestamp: 1530244444}
generate_consistent(user.id, user.timestamp)
=> "interesting-emu"
generate_consistent(user.id, user.timestamp)
=> "interesting-emu"
"""
@adjectives AnonymousNameGenerator.Adjective.adjectives
@adjective_count @adjectives |> tuple_size
@doc false
def adjectives, do: @adjectives
@doc false
def adjective_count, do: @adjective_count
@nouns AnonymousNameGenerator.Noun.nouns
@noun_count @nouns |> tuple_size
@doc false
def nouns, do: @nouns
@doc false
def noun_count, do: @noun_count
@default_num_possibilities @adjective_count * @noun_count
@doc """
This will create a new unique name on each call.
By default, the number of possibilities are:
number of adjectives(#{@adjective_count}) multiplied by the number of nouns(#{@noun_count}) = #{@adjective_count * @noun_count}
Passing a param will add a number if more possibilities are needed.
generate_random()
=> "cheery-pond"
generate_random(1_000_000)
=> "spicy-snowflake-5"
"""
@spec generate_random(integer | nil) :: String.t
def generate_random(num_possibilities \\ nil)
def generate_random(nil) do
adj = elem(@adjectives, :rand.uniform(@adjective_count) - 1)
noun = elem(@nouns, :rand.uniform(@noun_count) - 1)
adj <> "-" <> noun
end
def generate_random(num_possibilities) when is_integer(num_possibilities) do
nums = get_random_numbers_for(num_possibilities)
case nums do
nil -> generate_random
numbers -> generate_random <> "-" <> numbers
end
end
@doc """
This will always return the same result if the same params are passed.\n
This can be used for always giving the same name to a user.
For example `generate_consistent(user.id, user.inserted_at_unix_timestamp))`
generate_consistent(1, 2)
=> "damaged-quill"
generate_consistent(1, 2)
=> "damaged-quill"
generate_consistent(10, 11, 2_000_000)
=> "hidden-ghost-18"
generate_consistent(10, 11, 2_000_000)
=> "hidden-ghost-18"
"""
@spec generate_consistent(integer, integer, integer | nil) :: String.t
def generate_consistent(a, b, num_possibilities \\ nil)
def generate_consistent(a, b, nil) when is_integer(a) and is_integer(b) do
adj = elem(@adjectives, :erlang.phash2(a, @adjective_count))
noun = elem(@nouns, :erlang.phash2(b, @noun_count))
adj <> "-" <> noun
end
def generate_consistent(a, b, num) when is_integer(a) and is_integer(b) do
prefix = generate_consistent(a, b, nil)
suffix = get_consistent_numbers_for(a, b, num)
prefix <> "-" <> suffix
end
@spec get_random_numbers_for(integer) :: String.t | nil
defp get_random_numbers_for(num_possibilities) do
needed = numbers_needed_to_get_possibilities(num_possibilities)
if needed > 0 do
1..numbers_needed_to_get_possibilities(num_possibilities)
|> Enum.map(fn(_) -> :rand.uniform(10) - 1 end)
|> Enum.join("")
end
end
@spec numbers_needed_to_get_possibilities(integer) :: integer
defp numbers_needed_to_get_possibilities(num) when num <= @default_num_possibilities, do: 0
defp numbers_needed_to_get_possibilities(num) do
:math.log10(num / @default_num_possibilities)
|> Float.ceil
|> trunc
end
@spec get_consistent_numbers_for(integer, integer, integer) :: String.t
defp get_consistent_numbers_for(a, b, num_possibilities) do
nums_needed = numbers_needed_to_get_possibilities(num_possibilities)
a_binary = Integer.to_string(a, 2) |> String.pad_leading(8, "0")
b_binary = Integer.to_string(b, 2) |> String.pad_leading(8, "0")
binary = get_binary_string_to_create_n_numbers(a_binary <> b_binary, nums_needed)
create_n_numbers_from_binary(binary, nums_needed)
end
@spec get_binary_string_to_create_n_numbers(String.t, integer) :: String.t
defp get_binary_string_to_create_n_numbers(binary, num) do
str_len = num * 5
bin_len = binary |> String.length
dup_times = (str_len / bin_len) |> Float.ceil |> trunc
binary
|> String.duplicate(dup_times)
end
@spec create_n_numbers_from_binary(String.t, integer) :: String.t
defp create_n_numbers_from_binary(binary, num) do
binary
|> String.codepoints
|> Enum.chunk(5)
|> Enum.map(&Enum.join/1)
|> Enum.map(&(String.to_integer(&1, 2)))
|> Enum.map(&(rem &1, 10))
|> Enum.take(num)
|> Enum.join
end
end