Module: Ashid
- Extended by:
- Ashid
- Included in:
- Ashid
- Defined in:
- lib/ashid.rb,
lib/ashid/error.rb,
lib/ashid/encoder.rb,
lib/ashid/version.rb
Defined Under Namespace
Modules: Encoder
Classes: Error, InvalidEncodingError, InvalidIdError
Constant Summary
collapse
- MAX_TIMESTAMP =
35_184_372_088_831
- TIMESTAMP_ENCODED_LENGTH =
9
- RANDOM_ENCODED_LENGTH =
13
- STANDARD_BASE_LENGTH =
22
- ASHID4_BASE_LENGTH =
26
- VERSION =
"0.1.0"
Instance Method Summary
collapse
Instance Method Details
#generate(prefix = nil, time: current_time_ms, random: Encoder.secure_random(bits: 64)) ⇒ Object
16
17
18
19
20
21
22
23
24
|
# File 'lib/ashid.rb', line 16
def generate(prefix = nil, time: current_time_ms, random: Encoder.secure_random(bits: 64))
raise ArgumentError, "time must be non-negative" if time < 0
raise ArgumentError, "time must not exceed #{MAX_TIMESTAMP}" if time > MAX_TIMESTAMP
raise ArgumentError, "random must be non-negative" if random < 0
normalized = normalize_prefix(prefix)
base = build_base_id(normalized, time, random)
"#{normalized}#{base}"
end
|
#generate4(prefix = nil, random1: Encoder.secure_random(bits: 64), random2: Encoder.secure_random(bits: 64)) ⇒ Object
26
27
28
29
30
31
32
33
|
# File 'lib/ashid.rb', line 26
def generate4(prefix = nil, random1: Encoder.secure_random(bits: 64), random2: Encoder.secure_random(bits: 64))
raise ArgumentError, "random1 must be non-negative" if random1 < 0
raise ArgumentError, "random2 must be non-negative" if random2 < 0
normalized = normalize_prefix(prefix)
base = "#{Encoder.encode(random1, padded: true)}#{Encoder.encode(random2, padded: true)}"
"#{normalized}#{base}"
end
|
#normalize(id) ⇒ Object
103
104
105
106
107
108
109
|
# File 'lib/ashid.rb', line 103
def normalize(id)
parsed = parse(id)
norm_prefix = parsed[:prefix].empty? ? nil : parsed[:prefix].downcase.chomp("_")
time_value = Encoder.decode(parsed[:encoded_timestamp])
random_value = Encoder.decode(parsed[:encoded_random])
generate(norm_prefix, time: time_value, random: random_value)
end
|
#parse(id) ⇒ Object
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
# File 'lib/ashid.rb', line 35
def parse(id)
raise InvalidIdError, "id cannot be nil" if id.nil?
raise InvalidIdError, "id must be a String" unless id.is_a?(String)
raise InvalidIdError, "id cannot be empty" if id.empty?
prefix_length = 0
has_delimiter = false
id.each_char do |ch|
if ch.match?(/[a-zA-Z]/)
prefix_length += 1
elsif (ch == "_" || ch == "-") && prefix_length > 0
prefix_length += 1
has_delimiter = true
break
else
prefix_length = 0
break
end
end
prefix = has_delimiter ? id[0, prefix_length].sub(/-\z/, "_") : ""
base_id = has_delimiter ? id[prefix_length..] : id
raise InvalidIdError, "id must have a base ID" if base_id.nil? || base_id.empty?
encoded_timestamp, encoded_random = split_base(base_id, has_delimiter)
{ prefix: prefix, encoded_timestamp: encoded_timestamp, encoded_random: encoded_random }
end
|
#prefix(id) ⇒ Object
66
67
68
|
# File 'lib/ashid.rb', line 66
def prefix(id)
parse(id)[:prefix]
end
|
#random(id) ⇒ Object
80
81
82
|
# File 'lib/ashid.rb', line 80
def random(id)
Encoder.decode(parse(id)[:encoded_random])
end
|
#random_bytes(id) ⇒ Object
84
85
86
|
# File 'lib/ashid.rb', line 84
def random_bytes(id)
[random(id).to_s(16).rjust(16, "0")].pack("H*")
end
|
#time(id) ⇒ Object
Returns a Time. Note: ms-precision via float division to seconds; sub-ms detail is lost but ms is our resolution anyway.
76
77
78
|
# File 'lib/ashid.rb', line 76
def time(id)
Time.at(timestamp(id) / 1000.0)
end
|
#timestamp(id) ⇒ Object
70
71
72
|
# File 'lib/ashid.rb', line 70
def timestamp(id)
Encoder.decode(parse(id)[:encoded_timestamp])
end
|
#valid?(id) ⇒ Boolean
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
# File 'lib/ashid.rb', line 88
def valid?(id)
return false unless id.is_a?(String) && !id.empty?
parsed = parse(id)
if !parsed[:prefix].empty? && parsed[:prefix] !~ /\A[a-z0-9]+_\z/
return false
end
Encoder.decode(parsed[:encoded_timestamp])
Encoder.decode(parsed[:encoded_random])
true
rescue Error
false
end
|