onliest 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/onliest/snowflake.rb +42 -0
- data/lib/onliest.rb +16 -11
- data/test/{test_onliest.rb → test_onliest_default.rb} +9 -14
- data/test/test_onliest_snowflake.rb +54 -0
- metadata +41 -33
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -4
- metadata.gz.sig +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: efb038a4ae13fe3e96144555528df22c58069f09
|
4
|
+
data.tar.gz: 253159af40687da8125e46543ffbdf28a95cb646
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3ad14aebd918aabb5b046f4b769220ed41fa4f668371b84126e084ca6522d98f47d109adf0099af6a426610bdb99ba5b23f8f67e23e5e7b9d1a555e67160df2
|
7
|
+
data.tar.gz: 976cc81e29400a059d515b6406ceb17ce6d55a10463b839d1128ab1a21eb71325b92eb3895f6fd67fa855a352a250fed769427a8e191acb331c2bdaea504b137
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'onliest'
|
2
|
+
|
3
|
+
class Onliest::Snowflake < Onliest
|
4
|
+
attr_reader :worker_number, :sequence
|
5
|
+
|
6
|
+
# This comes from twitter snowflake. I'm not
|
7
|
+
# sure how they arrived at this number.
|
8
|
+
# November 3, 2010 at 11:42:55
|
9
|
+
EPOCH = 1288834974657
|
10
|
+
|
11
|
+
def initialize(worker_number)
|
12
|
+
@worker_number = worker_number
|
13
|
+
@sequence = 0
|
14
|
+
@older_time = 0
|
15
|
+
super(fields: [{ bits: 1, generator: ->{ 0 } },
|
16
|
+
{ bits: 41, generator: ->{ time } },
|
17
|
+
{ bits: 10, generator: ->{ worker_number } },
|
18
|
+
{ bits: 12, generator: ->{ sequence_nextval } }])
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def sequence_nextval
|
24
|
+
i = @sequence
|
25
|
+
@sequence = @sequence.succ
|
26
|
+
i
|
27
|
+
end
|
28
|
+
|
29
|
+
def time
|
30
|
+
while (t = time_now) < @older_time
|
31
|
+
#puts @older_time
|
32
|
+
#puts t
|
33
|
+
sleep((@older_time - t) / 1000)
|
34
|
+
end
|
35
|
+
@older_time = t
|
36
|
+
t
|
37
|
+
end
|
38
|
+
|
39
|
+
def time_now
|
40
|
+
((Time.now.to_f * 1000).to_i - EPOCH)
|
41
|
+
end
|
42
|
+
end
|
data/lib/onliest.rb
CHANGED
@@ -24,23 +24,28 @@ class Onliest
|
|
24
24
|
# +SecureRandom+. An object that implements +:random_number+
|
25
25
|
# returning a random integer >= 0 and less than value provided as
|
26
26
|
# the first argument.
|
27
|
-
def initialize(rng
|
28
|
-
@
|
27
|
+
def initialize(rng: DEFAULT_RNG, fields: false)
|
28
|
+
@fields = fields ? fields : default_fields(rng)
|
29
29
|
end
|
30
30
|
|
31
|
-
#
|
31
|
+
# Construct and return the unique value
|
32
32
|
def value
|
33
|
-
|
34
|
-
|
33
|
+
total_bits = 0
|
34
|
+
total_value = 0
|
35
|
+
@fields.reverse.each do |field|
|
36
|
+
bits = field.fetch(:bits)
|
37
|
+
field_value = Integer(field.fetch(:generator).call) & ((2**bits) - 1)
|
38
|
+
total_value += (field_value << total_bits)
|
39
|
+
total_bits += bits
|
40
|
+
end
|
41
|
+
total_value
|
35
42
|
end
|
36
43
|
|
37
44
|
private
|
38
45
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
def some_time_bits
|
44
|
-
Time.now.to_i & TIME_BITMASK
|
46
|
+
def default_fields(rng)
|
47
|
+
[{ bits: TIME_BITS, generator: -> { Time.now.to_i } },
|
48
|
+
{ bits: RANDOM_BITS,
|
49
|
+
generator: -> { rng.random_number(RANDOM_BITMASK + 1) } }]
|
45
50
|
end
|
46
51
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative './test_setup'
|
3
2
|
require 'onliest'
|
4
3
|
|
5
|
-
|
4
|
+
# Tests for the dkjefault scheme
|
5
|
+
|
6
|
+
class OnliestDefaultTest < TestCase
|
6
7
|
def test_generates_a_number
|
7
8
|
assert_kind_of(Integer, Onliest.value)
|
8
9
|
end
|
@@ -16,31 +17,25 @@ class OnliestTest < Minitest::Unit::TestCase
|
|
16
17
|
fake_prng.expect(:random_number, value, [2**47])
|
17
18
|
end
|
18
19
|
|
19
|
-
def at(value)
|
20
|
-
Time.stub(:now, Time.at(value)) do
|
21
|
-
yield
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
20
|
def test_with_a_time_and_a_prng
|
26
21
|
at((2**25) + 1) do
|
27
22
|
random_value = 2
|
28
|
-
gen = Onliest.new(fake_prng(random_value))
|
29
|
-
assert_equal(
|
23
|
+
gen = Onliest.new(rng: fake_prng(random_value))
|
24
|
+
assert_equal((1 << 47) + random_value, gen.value)
|
30
25
|
end
|
31
26
|
end
|
32
27
|
|
33
28
|
def test_the_littlest_onliest
|
34
29
|
at(0) do
|
35
|
-
gen = Onliest.new(fake_prng(0))
|
30
|
+
gen = Onliest.new(rng: fake_prng(0))
|
36
31
|
assert_equal(gen.value, 0)
|
37
32
|
end
|
38
33
|
end
|
39
34
|
|
40
35
|
def test_the_largest_onliest
|
41
36
|
at(2**25 - 1) do
|
42
|
-
gen = Onliest.new(fake_prng(2**47 - 1))
|
43
|
-
assert_equal(
|
37
|
+
gen = Onliest.new(rng: fake_prng(2**47 - 1))
|
38
|
+
assert_equal(2**72 - 1, gen.value)
|
44
39
|
end
|
45
40
|
end
|
46
41
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative './test_setup'
|
2
|
+
require 'onliest/snowflake'
|
3
|
+
|
4
|
+
class SnowflakeTests < TestCase
|
5
|
+
def worker_number
|
6
|
+
'13'
|
7
|
+
end
|
8
|
+
|
9
|
+
def generator
|
10
|
+
Onliest::Snowflake.new(worker_number)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_snowflake_sign_bit_is_zero
|
14
|
+
assert_equal(0, generator.value & (1 << 63))
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_snowflake_time_with_millisecond_precision
|
18
|
+
at(1.1234 + (Onliest::Snowflake::EPOCH.to_f / 1000)) do
|
19
|
+
assert_equal(1123, generator.value >> 22)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_snowflake_worker_number
|
24
|
+
assert_equal(worker_number.to_i, (generator.value & (2**22 - 1)) >> 12)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_snowflake_sequence_starts_at_zero
|
28
|
+
assert_equal(0, generator.value & ((2**12 - 1)))
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_snowflake_sequence_increments
|
32
|
+
at(Time.now.to_i) do
|
33
|
+
gen = generator
|
34
|
+
assert_equal(gen.value, gen.value - 1)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_snowflake_epoch_starts_at_1288834974_657
|
39
|
+
at(1288834974657.to_f / 1000) do
|
40
|
+
assert_equal(0, generator.value >> 22)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_time_always_goes_forward
|
45
|
+
gen = generator
|
46
|
+
times = [Onliest::Snowflake::EPOCH + 0.10,
|
47
|
+
Onliest::Snowflake::EPOCH,
|
48
|
+
Onliest::Snowflake::EPOCH + 0.11]
|
49
|
+
|
50
|
+
Time.stub(:now, ->{ times.shift } ) do
|
51
|
+
assert(gen.value < gen.value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
metadata
CHANGED
@@ -1,37 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: onliest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Church
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
|
-
cert_chain:
|
11
|
-
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5'
|
35
41
|
description: Generate unique values with numeric locality.
|
36
42
|
email: chrchr@gmail.com
|
37
43
|
executables:
|
@@ -39,9 +45,11 @@ executables:
|
|
39
45
|
extensions: []
|
40
46
|
extra_rdoc_files: []
|
41
47
|
files:
|
42
|
-
- lib/onliest.rb
|
43
48
|
- bin/onliest
|
44
|
-
-
|
49
|
+
- lib/onliest.rb
|
50
|
+
- lib/onliest/snowflake.rb
|
51
|
+
- test/test_onliest_default.rb
|
52
|
+
- test/test_onliest_snowflake.rb
|
45
53
|
homepage: https://github.com/chrchr/onliest
|
46
54
|
licenses:
|
47
55
|
- MIT
|
@@ -52,20 +60,20 @@ require_paths:
|
|
52
60
|
- lib
|
53
61
|
required_ruby_version: !ruby/object:Gem::Requirement
|
54
62
|
requirements:
|
55
|
-
- -
|
63
|
+
- - ">="
|
56
64
|
- !ruby/object:Gem::Version
|
57
65
|
version: '0'
|
58
66
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
67
|
requirements:
|
60
|
-
- -
|
68
|
+
- - ">="
|
61
69
|
- !ruby/object:Gem::Version
|
62
70
|
version: '0'
|
63
71
|
requirements: []
|
64
72
|
rubyforge_project:
|
65
|
-
rubygems_version: 2.
|
73
|
+
rubygems_version: 2.4.5
|
66
74
|
signing_key:
|
67
75
|
specification_version: 4
|
68
76
|
summary: 'Onliest: generate unique values with numeric locality'
|
69
77
|
test_files:
|
70
|
-
- test/
|
71
|
-
|
78
|
+
- test/test_onliest_default.rb
|
79
|
+
- test/test_onliest_snowflake.rb
|
checksums.yaml.gz.sig
DELETED
Binary file
|
data.tar.gz.sig
DELETED
metadata.gz.sig
DELETED