Skip to content

Commit b76a8ba

Browse files
committed
fix warnings, add doc
1 parent 1189e8f commit b76a8ba

File tree

4 files changed

+145
-55
lines changed

4 files changed

+145
-55
lines changed

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@ unimportant validation codes and stuff like that.
1313
Notes
1414
=====
1515

16-
Strings will only contain a-z, A-Z and 0-9 characters.
16+
Strings will only contain a-z, A-Z and 0-9 characters and will always start with
17+
a character.
1718

1819
Numbers may be shorter than length. (Due to possible start with 0)
1920

2021
The whole thing relies on Erlang's random:uniform/1 - I don't know if this is a
2122
secure PRNG or not, so better do not use this module for security stuff.
2223

23-
Todo
24-
====
24+
Motivation
25+
============
2526

26-
Add documentation, generate String of numbers.
27+
1. Detects OTP version and uses `:random.seed` or `:rand.seed` with either
28+
`:erlang.now` or `:erlang.monotonic_time` as both the rand module and using
29+
erland.now got deprecated in OTP 17 and 18.
30+
31+
2. I use it all the time.
2732

28-
LICENSE
33+
License
2934
=======
3035
MIT

lib/random.ex

Lines changed: 106 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,119 @@
11
defmodule Misc.Random do
22

3-
def seed_random do
4-
use_monotonic = :erlang.module_info
5-
|> Keyword.get( :exports )
6-
|> Keyword.get( :monotonic_time )
7-
time_bif = case use_monotonic do
8-
1 -> &:erlang.monotonic_time/0
9-
nil -> &:erlang.now/0
10-
end
11-
:random.seed( time_bif.() )
3+
def seed_random do
4+
try do
5+
get_otp_version() |> seed()
6+
catch
7+
_, _ -> seed()
128
end
9+
end
1310

14-
def string( length ) do
15-
get_string( length )
16-
end
11+
def get_otp_version do
12+
:otp_release
13+
|> :erlang.system_info()
14+
|> to_string()
15+
|> Integer.parse()
16+
|> elem(0)
17+
end
1718

18-
def string() do
19-
get_string( 8 )
20-
end
19+
# this work on OTP 17 w/o warnings
20+
def seed do
21+
:erlang.now() |> :random.seed()
22+
end
2123

22-
def get_string( length ) do
23-
seed_random
24-
alphabet
25-
= "abcdefghijklmnopqrstuvwxyz"
26-
<> "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
27-
<> "0123456789"
28-
alphabet_length =
29-
alphabet |> String.length
30-
31-
1..length
32-
|> Enum.map_join(
33-
fn(_) ->
34-
alphabet |> String.at( :random.uniform( alphabet_length ) - 1 )
35-
end
36-
)
37-
end
24+
def seed(otp_version) when otp_version <= 17 do
25+
seed()
26+
end
3827

39-
def number() do
40-
get_number(8)
41-
end
28+
# use of erlang.now is deprecated in OTP 18
29+
def seed(otp_version) when otp_version == 18 do
30+
:erlang.monotonic_time() |> :random.seed()
31+
end
4232

43-
def number( length ) do
44-
get_number( length )
45-
end
33+
# random is deperecated for rand in OTP 19
34+
def seed(otp_version) when otp_version >= 19 do
35+
now = {
36+
:erlang.phash2([node()]),
37+
:erlang.monotonic_time(),
38+
:erlang.unique_integer()
39+
}
40+
:rand.seed(:exsplus, now)
41+
end
42+
43+
@doc """
44+
Generates a random string that will always start with a character.
45+
"""
46+
def string(length \\ 8) do
47+
seed_random()
48+
get_string(length)
49+
end
50+
51+
defp get_string(1) do
52+
default_alphabet(:alpha) |> get_random_char
53+
end
54+
55+
defp get_string(length) when length > 1 do
56+
first = get_string(1)
57+
rest = Enum.map_join(2..length, fn _ -> default_alphabet() |> get_random_char() end)
58+
first <> rest
59+
end
60+
61+
def get_random_char(alphabet) do
62+
alphabet_length = alphabet |> String.length
63+
get_random_char(alphabet, alphabet_length)
64+
end
4665

47-
def get_number( length ) do
48-
seed_random
66+
def get_random_char(alphabet, alphabet_length) do
67+
alphabet |> String.at(uniform(alphabet_length) - 1)
68+
end
4969

50-
{ number, "" } =
51-
Integer.parse 1..length
52-
|> Enum.map_join( fn(_) -> :random.uniform(10) - 1 end )
53-
number
70+
@doc """
71+
Generates a random number as string that will not start with leading zeroes.
72+
"""
73+
def number(length \\ 8) do
74+
seed_random()
75+
length
76+
|> get_number()
77+
|> Integer.parse()
78+
|> elem(0)
79+
end
80+
81+
defp get_number(1) do
82+
default_alphabet(:numeric_wo_zeroes) |> get_random_char()
83+
end
84+
85+
defp get_number(length) when is_integer(length) do
86+
first = get_number(1)
87+
rest = Enum.map_join(2..length, fn _ -> default_alphabet(:numeric) |> get_random_char end)
88+
first <> rest
89+
end
90+
91+
def default_alphabet do
92+
default_alphabet(:alpha) <> default_alphabet(:numeric)
93+
end
94+
95+
def default_alphabet(:alpha) do
96+
"abcdefghijklmnopqrstuvwxyz" <> "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
97+
end
98+
99+
def default_alphabet(:numeric) do
100+
default_alphabet(:numeric_wo_zeroes) <> "0"
101+
end
102+
103+
def default_alphabet(:numeric_wo_zeroes) do
104+
"123456789"
105+
end
106+
107+
def uniform(x) do
108+
try do
109+
get_otp_version() |> get_uniform(x)
110+
catch
111+
_, _ -> get_uniform(x)
54112
end
113+
end
114+
115+
def get_uniform(x), do: x |> :random.uniform
116+
def get_uniform(otp_version, x) when otp_version <= 18, do: x |> get_uniform(x)
117+
def get_uniform(otp_version, x) when is_integer(otp_version), do: x |> :rand.uniform
55118

56119
end

mix.exs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ defmodule Misc.Random.Mixfile do
33

44
def project do
55
[app: :misc_random,
6-
version: "0.2.6",
6+
version: "0.2.8",
77
elixir: "> 1.0.0",
8-
description: description,
9-
package: package,
10-
deps: deps]
8+
description: description(),
9+
package: package(),
10+
deps: deps()]
1111
end
1212

1313
# Configuration for the OTP application
@@ -32,11 +32,11 @@ defmodule Misc.Random.Mixfile do
3232

3333
defp description do
3434
"""
35-
This is a very thin wrapper around erlang's random:uniform method. It allows
36-
you to create random strings or numbers.
35+
Creates random numbers and strings of arbitrary lengths. Uses preferred
36+
Erlang random module based on used OTP version.
3737
"""
3838
end
39-
39+
4040
defp package do
4141
[# These are the default files included in the package
4242
files: ["lib", "mix.exs", "README*", "LICENSE*"],

test/random_test.exs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,28 @@ defmodule Misc.RandomTest do
1313
assert String.length( string ) == 8
1414
end
1515

16+
test "strings always start with a character" do
17+
string_starts_with_character = fn length ->
18+
length
19+
|> Misc.Random.string
20+
|> String.match?(~r/\A[a-z]/i)
21+
end
22+
1..100 |> Enum.all?(string_starts_with_character) |> assert
23+
end
24+
25+
test "numbers have desired length (no leading zeroes)" do
26+
numbers_have_desired_length = fn length ->
27+
number = length |> Misc.Random.number
28+
length_ok = number |> to_string |> String.length |> Kernel.==(length)
29+
start_ok = number |> to_string |> String.match?(~r/\A[1-9]/)
30+
if not length_ok do
31+
IO.inspect([length, number])
32+
end
33+
start_ok and length_ok
34+
end
35+
1..100 |> Enum.all?(numbers_have_desired_length) |> assert
36+
end
37+
1638
test "generate number" do
1739
number = Misc.Random.number()
1840
assert is_integer number

0 commit comments

Comments
 (0)