ulid 0.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ac96b57e8fb7ee4afc17adb9070d46828667155a
4
- data.tar.gz: f06ece64720bd8d8b5323ee69583e8ae3340562d
2
+ SHA256:
3
+ metadata.gz: c072f00beeac3732fbbee2fe32af5f95e14a4eb516e659f70e9e08c0bc525e01
4
+ data.tar.gz: f8d7ad8d5e714c142c0309a6e814be406c61519cc6e36adcaccae4982d94b96f
5
5
  SHA512:
6
- metadata.gz: b28852c6c6e9ac75f331f6330338b9b49c36bc12f346e7d3cddc84f8704cfe7ced0c129c893222f9437855f97b829d1bb29573a94418c387f20afb1c8641dfde
7
- data.tar.gz: d5d3ae42a1b1ed786ba3b87e1c9844fe6f89b41521a5e96782cda05a4beb8436c955ebb306517a1cf121f9eeb47a66822cbe91a8898653bdb5d08adfedd24335
6
+ metadata.gz: 0e7aaedb437ae82765ec74cf36ddbeb5b529948c9c65c077a0e648156c21ebb23dc26f2e85b3430434dd1babfaaa1b4b1d8bfd00c8dbf9f09f332bad33b93f97
7
+ data.tar.gz: 01f353360ca4b82534de75f8e715d872b9c976a85655a9f419dfc1a93b6e867d0bea342ad970a3b4653048f3484bba1bc8842f75980142869d13005d3fabc1e7
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: [rafaelsales]
@@ -0,0 +1,20 @@
1
+ name: Ruby
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+
8
+ runs-on: ubuntu-latest
9
+
10
+ steps:
11
+ - uses: actions/checkout@v2
12
+ - name: Set up Ruby 2.6
13
+ uses: actions/setup-ruby@v1
14
+ with:
15
+ ruby-version: 2.6.x
16
+ - name: Build and test with Rake
17
+ run: |
18
+ gem install bundler
19
+ bundle install --jobs 4 --retry 3
20
+ bundle exec rake test
data/.rubocop.yml ADDED
@@ -0,0 +1,26 @@
1
+ Style/Documentation:
2
+ Enabled: false
3
+
4
+ Style/TrailingCommaInArguments:
5
+ EnforcedStyleForMultiline: comma
6
+
7
+ Style/TrailingCommaInArrayLiteral:
8
+ EnforcedStyleForMultiline: comma
9
+
10
+ Style/TrailingCommaInHashLiteral:
11
+ EnforcedStyleForMultiline: comma
12
+
13
+ Metrics/AbcSize:
14
+ Enabled: false
15
+
16
+ BlockLength:
17
+ Enabled: false
18
+
19
+ Metrics/LineLength:
20
+ Max: 120
21
+
22
+ Metrics/MethodLength:
23
+ Enabled: false
24
+
25
+ Style/RescueStandardError:
26
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # 1.2.0
2
+
3
+ - PR #20 - Use an array to improve speed / reduce memory allocations. Thanks, @jamescook
4
+
5
+ # 1.1.1
6
+
7
+ - PR #19 - Remove Timecop gem. Thanks, @bquorning
8
+
9
+ - PR #18 - Remove Mocha gem. Thanks, @bquorning
10
+
11
+ - PR #17 - Add post_install_message to install sysrandom gem
12
+
13
+ - PR #16 - Remove circular require warning. Thanks, @pocke
14
+
15
+ # 1.1.0
16
+
17
+ - PR #14 - Ruby 2.5+ does not need sysrandom. Thanks, @sakuro
18
+
19
+ # 1.0.0
20
+
21
+ - PR #10 - Fix time encoding. Thanks, @dcuddeback
data/Gemfile CHANGED
@@ -3,13 +3,12 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  group :development, :test do
6
- gem "rake", "~> 10.0"
7
- gem "pry-byebug"
6
+ gem 'pry-byebug'
7
+ gem 'rake'
8
+ gem 'rubocop'
8
9
  end
9
10
 
10
11
  group :test do
11
- gem "minitest"
12
- gem "mocha"
13
- gem "timecop"
14
- gem "base32"
12
+ gem 'base32-crockford'
13
+ gem 'minitest'
15
14
  end
data/Gemfile.lock CHANGED
@@ -1,43 +1,50 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ulid (0.1.0)
5
- sysrandom (>= 1.0.0, < 2.0)
4
+ ulid (1.2.0)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
9
8
  specs:
10
- base32 (0.3.2)
11
- byebug (9.0.5)
12
- coderay (1.1.1)
13
- metaclass (0.0.4)
14
- method_source (0.8.2)
15
- minitest (5.9.0)
16
- mocha (1.1.0)
17
- metaclass (~> 0.0.1)
18
- pry (0.10.4)
9
+ ast (2.3.0)
10
+ base32-crockford (0.1.0)
11
+ byebug (9.1.0)
12
+ coderay (1.1.2)
13
+ method_source (0.9.0)
14
+ minitest (5.10.3)
15
+ parallel (1.12.0)
16
+ parser (2.4.0.0)
17
+ ast (~> 2.2)
18
+ powerpack (0.1.1)
19
+ pry (0.11.3)
19
20
  coderay (~> 1.1.0)
20
- method_source (~> 0.8.1)
21
- slop (~> 3.4)
22
- pry-byebug (3.4.0)
23
- byebug (~> 9.0)
21
+ method_source (~> 0.9.0)
22
+ pry-byebug (3.5.1)
23
+ byebug (~> 9.1)
24
24
  pry (~> 0.10)
25
- rake (10.5.0)
26
- slop (3.6.0)
27
- sysrandom (1.0.3)
28
- timecop (0.8.1)
25
+ rainbow (2.2.2)
26
+ rake
27
+ rake (13.0.1)
28
+ rubocop (0.50.0)
29
+ parallel (~> 1.10)
30
+ parser (>= 2.3.3.1, < 3.0)
31
+ powerpack (~> 0.1)
32
+ rainbow (>= 2.2.2, < 3.0)
33
+ ruby-progressbar (~> 1.7)
34
+ unicode-display_width (~> 1.0, >= 1.0.1)
35
+ ruby-progressbar (1.8.3)
36
+ unicode-display_width (1.3.0)
29
37
 
30
38
  PLATFORMS
31
39
  ruby
32
40
 
33
41
  DEPENDENCIES
34
- base32
42
+ base32-crockford
35
43
  minitest
36
- mocha
37
44
  pry-byebug
38
- rake (~> 10.0)
39
- timecop
45
+ rake
46
+ rubocop
40
47
  ulid!
41
48
 
42
49
  BUNDLED WITH
43
- 1.12.5
50
+ 2.0.1
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
+ ![Ruby](https://github.com/rafaelsales/ulid/workflows/Ruby/badge.svg)
2
+ [![Gem Downloads](http://img.shields.io/gem/dt/ulid.svg)](https://rubygems.org/gems/ulid)
3
+ [![GitHub License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/rafaelsales/ulid)
4
+
1
5
  # ulid
2
6
  Universally Unique Lexicographically Sortable Identifier implementation for Ruby
3
7
 
8
+ Official specification page: https://github.com/ulid/spec
9
+
4
10
  <h1 align="center">
5
11
  <br>
6
12
  <br>
@@ -10,9 +16,6 @@ Universally Unique Lexicographically Sortable Identifier implementation for Ruby
10
16
  <br>
11
17
  </h1>
12
18
 
13
- [![Gem Downloads](http://img.shields.io/gem/dt/ulid.svg)](https://rubygems.org/gems/ulid)
14
- [![GitHub License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/rafaelsales/ulid)
15
-
16
19
  # Universally Unique Lexicographically Sortable Identifier
17
20
 
18
21
  UUID can be suboptimal for many uses-cases because:
@@ -82,9 +85,9 @@ The components are encoded as 16 octets. Each component is encoded with the Most
82
85
  0 1 2 3
83
86
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
84
87
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
85
- | 32_bit_uint_time_low |
88
+ | 32_bit_uint_time_high |
86
89
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
87
- | 16_bit_uint_time_high | 16_bit_uint_random |
90
+ | 16_bit_uint_time_low | 16_bit_uint_random |
88
91
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
89
92
  | 32_bit_uint_random |
90
93
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -110,5 +113,4 @@ bundle exec rake test
110
113
 
111
114
  ### Credits and references:
112
115
 
113
- * https://github.com/alizain/ulid
114
- * https://github.com/ulid-org/spec
116
+ * https://github.com/ulid/javascript
data/Rakefile CHANGED
@@ -1,15 +1,15 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
3
  require 'benchmark'
4
4
  require 'ulid'
5
5
 
6
6
  Rake::TestTask.new do |t|
7
- t.libs += ["spec", "lib"]
7
+ t.libs += %w[spec lib]
8
8
  t.test_files = FileList['spec/**/*_spec.rb']
9
9
  end
10
10
 
11
- desc "Benchmark base32 ULID generation (default 100,000 iterations)"
12
- task :benchmark, [:iterations] do |t, args|
11
+ desc 'Benchmark base32 ULID generation (default 100,000 iterations)'
12
+ task :benchmark, [:iterations] do |_t, args|
13
13
  iterations = (args[:iterations] || 100_000).to_i
14
14
  Benchmark.bm do |b|
15
15
  b.report("#{iterations} iterations") { iterations.times { ULID.generate } }
data/lib/ulid.rb CHANGED
@@ -1,4 +1,5 @@
1
- Dir.glob(File.join(File.dirname(__FILE__), "/**/*.rb")).sort.each { |f| require f }
1
+ require 'ulid/version'
2
+ require 'ulid/generator'
2
3
 
3
4
  module ULID
4
5
  extend Generator
@@ -1,41 +1,70 @@
1
- require 'sysrandom'
1
+ # frozen-string-literal: true
2
+
3
+ if RUBY_VERSION >= '2.5'
4
+ require 'securerandom'
5
+ else
6
+ require 'sysrandom/securerandom'
7
+ end
2
8
 
3
9
  module ULID
4
10
  module Generator
5
- ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ' # Crockford's Base32
11
+ ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'.bytes.freeze # Crockford's Base32
6
12
  RANDOM_BYTES = 10
7
13
  ENCODED_LENGTH = 26
8
14
  BIT_LENGTH = 128
9
15
  BITS_PER_B32_CHAR = 5
16
+ ZERO = '0'.ord
10
17
 
11
18
  MASK = 0x1f
12
19
 
13
- def generate
14
- input = octo_word
15
- (1..ENCODED_LENGTH).to_a.reduce("") do |s, n|
16
- shift = BIT_LENGTH - BITS_PER_B32_CHAR * n
17
- s + ENCODING[(input >> shift) & MASK]
18
- end
20
+ def generate(time = Time.now)
21
+ input = octo_word(time)
22
+
23
+ encode(input, ENCODED_LENGTH)
19
24
  end
20
25
 
21
- def generate_bytes
22
- time_48bit + random_bytes
26
+ def generate_bytes(time = Time.now)
27
+ time_48bit(time) + random_bytes
23
28
  end
24
29
 
25
30
  private
26
31
 
27
- def octo_word
28
- (hi, lo) = generate_bytes.unpack("Q>Q>")
32
+ def encode(input, length)
33
+ e = Array.new(length, ZERO)
34
+ i = length - 1
35
+
36
+ while input > 0
37
+ e[i] = ENCODING[input & MASK]
38
+ input >>= 5
39
+ i -= 1
40
+ end
41
+
42
+ e.pack('c*')
43
+ end
44
+
45
+ def octo_word(time = Time.now)
46
+ (hi, lo) = generate_bytes(time).unpack('Q>Q>')
29
47
  (hi << 64) | lo
30
48
  end
31
49
 
32
- def time_48bit
33
- hundred_micro_time = Time.now_100usec
34
- [hundred_micro_time].pack("Q>")[2..-1]
50
+ def time_48bit(time = Time.now)
51
+ # Avoid `time.to_f` since we want to accurately represent a whole number of milliseconds:
52
+ #
53
+ # > time = Time.new(2020, 1, 5, 7, 3, Rational(2, 1000))
54
+ # => 2020-01-05 07:03:00 +0000
55
+ # > (time.to_f * 1000).to_i
56
+ # => 1578207780001
57
+ #
58
+ # vs
59
+ #
60
+ # > (time.to_r * 1000).to_i
61
+ # => 1578207780002
62
+ time_ms = (time.to_r * 1000).to_i
63
+ [time_ms].pack('Q>')[2..-1]
35
64
  end
36
65
 
37
66
  def random_bytes
38
- Sysrandom.random_bytes(RANDOM_BYTES)
67
+ SecureRandom.random_bytes(RANDOM_BYTES)
39
68
  end
40
69
  end
41
70
  end
data/lib/ulid/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ULID
2
- VERSION = '0.1.0'
2
+ VERSION = '1.3.0'.freeze
3
3
  end
@@ -1,51 +1,71 @@
1
1
  require 'spec_helper'
2
- require 'timecop'
3
- require 'base32'
2
+ require 'base32/crockford'
4
3
 
5
4
  describe ULID do
6
- describe "textual representation" do
7
- it "ensures it has 26 chars" do
5
+ describe 'textual representation' do
6
+ it 'ensures it has 26 chars' do
8
7
  ulid = ULID.generate
9
8
 
10
9
  ulid.length.must_equal 26
11
10
  end
12
11
 
13
- it "is sortable" do
14
- ulid_1, ulid_2 = nil
15
- Timecop.freeze do
16
- ulid_1 = ULID.generate
17
- Timecop.travel Time.now + 1
18
- ulid_2 = ULID.generate
19
- end
20
- assert ulid_2 > ulid_1
12
+ it 'is sortable' do
13
+ input_time = Time.now
14
+ ulid1 = ULID.generate(input_time)
15
+ ulid2 = ULID.generate(input_time + 1)
16
+ assert ulid2 > ulid1
21
17
  end
22
18
 
23
- it "is valid Crockford Base32" do
24
- Base32.table = ULID::Generator::ENCODING
19
+ it 'is valid Crockford Base32' do
25
20
  ulid = ULID.generate
26
- decoded = Base32.decode(ulid)
27
- encoded = Base32.encode(decoded)[0...26]
28
- assert encoded == ulid
21
+ decoded = Base32::Crockford.decode(ulid)
22
+ encoded = Base32::Crockford.encode(decoded, length: 26)
23
+ assert_equal ulid, encoded
24
+ end
25
+
26
+ it 'encodes the timestamp in the first 10 characters' do
27
+ # test case taken from original ulid README:
28
+ # https://github.com/ulid/javascript#seed-time
29
+ #
30
+ # N.b. we avoid specifying the time as a float, since we lose precision:
31
+ #
32
+ # > Time.at(1_469_918_176.385).strftime("%F %T.%N")
33
+ # => "2016-07-30 23:36:16.384999990"
34
+ #
35
+ # vs the correct:
36
+ #
37
+ # > Time.at(1_469_918_176, 385, :millisecond).strftime("%F %T.%N")
38
+ # => "2016-07-30 23:36:16.385000000"
39
+ ulid = ULID.generate(Time.at(1_469_918_176, 385, :millisecond))
40
+ assert_equal '01ARYZ6S41', ulid[0...10]
29
41
  end
30
- end
31
42
 
32
- describe "underlying binary" do
43
+ it 'respects millisecond-precision order' do
44
+ ulids = Array.new(1000) do |millis|
45
+ time = Time.new(2020, 1, 2, 3, 4, Rational(millis, 10**3))
33
46
 
34
- it "encodes the timestamp in the high 48 bits" do
35
- Timecop.freeze do
36
- now_100usec = Time.now_100usec
37
- bytes = ULID.generate_bytes
38
- ts = ("\x0\x0" + bytes[0...6]).unpack("Q>").first
39
- assert ts == now_100usec
47
+ ULID.generate(time)
40
48
  end
41
- end
42
49
 
43
- it "encodes the remaining 80 bits as random" do
44
- random_bytes = Sysrandom.random_bytes(ULID::Generator::RANDOM_BYTES)
45
- ULID.stubs(:random_bytes).returns(random_bytes)
46
- bytes = ULID.generate_bytes
47
- assert bytes[6..-1] == random_bytes
50
+ assert_equal(ulids, ulids.sort)
48
51
  end
49
52
  end
50
53
 
54
+ describe 'underlying binary' do
55
+ it 'encodes the timestamp in the high 48 bits' do
56
+ input_time = Time.now.utc
57
+ bytes = ULID.generate_bytes(input_time)
58
+ (time_ms,) = "\x0\x0#{bytes[0...6]}".unpack('Q>')
59
+ encoded_time = Time.at(time_ms / 1000.0).utc
60
+ assert_in_delta input_time, encoded_time, 0.001
61
+ end
62
+
63
+ it 'encodes the remaining 80 bits as random' do
64
+ random_bytes = SecureRandom.random_bytes(ULID::Generator::RANDOM_BYTES)
65
+ ULID.stub(:random_bytes, random_bytes) do
66
+ bytes = ULID.generate_bytes
67
+ assert bytes[6..-1] == random_bytes
68
+ end
69
+ end
70
+ end
51
71
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'pry'
2
2
  require 'minitest/autorun'
3
3
  require 'minitest/pride'
4
- require 'mocha/mini_test'
5
4
  require 'ulid'
data/ulid.gemspec CHANGED
@@ -1,21 +1,23 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'ulid/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "ulid"
6
+ spec.name = 'ulid'
8
7
  spec.version = ULID::VERSION
9
- spec.authors = ["Rafael Sales"]
10
- spec.email = ["rafaelcds@gmail.com"]
11
- spec.summary = %q{Universally Unique Lexicographically Sortable Identifier implementation for Ruby}
12
- spec.homepage = "https://github.com/rafaelsales/ulid"
13
- spec.license = "MIT"
8
+ spec.authors = ['Rafael Sales']
9
+ spec.email = ['rafaelcds@gmail.com']
10
+ spec.summary = 'Universally Unique Lexicographically Sortable Identifier implementation for Ruby'
11
+ spec.homepage = 'https://github.com/rafaelsales/ulid'
12
+ spec.license = 'MIT'
14
13
 
15
14
  spec.files = `git ls-files -z`.split("\x0")
16
15
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
16
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
17
+ spec.require_paths = ['lib']
19
18
 
20
- spec.add_dependency "sysrandom", ">= 1.0.0", "< 2.0"
19
+ spec.post_install_message = '
20
+ ulid gem needs to install sysrandom gem if you use Ruby 2.4 or older.
21
+ Execute `gem install sysrandom` or add `gem "sysrandom"` to Gemfile.
22
+ '
21
23
  end
metadata CHANGED
@@ -1,49 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ulid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael Sales
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-29 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: sysrandom
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 1.0.0
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '2.0'
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 1.0.0
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '2.0'
33
- description:
11
+ date: 2021-03-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
34
14
  email:
35
15
  - rafaelcds@gmail.com
36
16
  executables: []
37
17
  extensions: []
38
18
  extra_rdoc_files: []
39
19
  files:
20
+ - ".github/FUNDING.yml"
21
+ - ".github/workflows/ruby.yml"
40
22
  - ".gitignore"
23
+ - ".rubocop.yml"
24
+ - CHANGELOG.md
41
25
  - Gemfile
42
26
  - Gemfile.lock
43
27
  - LICENSE
44
28
  - README.md
45
29
  - Rakefile
46
- - lib/ext/time.rb
47
30
  - lib/ulid.rb
48
31
  - lib/ulid/generator.rb
49
32
  - lib/ulid/version.rb
@@ -55,7 +38,10 @@ homepage: https://github.com/rafaelsales/ulid
55
38
  licenses:
56
39
  - MIT
57
40
  metadata: {}
58
- post_install_message:
41
+ post_install_message: |2
42
+
43
+ ulid gem needs to install sysrandom gem if you use Ruby 2.4 or older.
44
+ Execute `gem install sysrandom` or add `gem "sysrandom"` to Gemfile.
59
45
  rdoc_options: []
60
46
  require_paths:
61
47
  - lib
@@ -70,9 +56,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
56
  - !ruby/object:Gem::Version
71
57
  version: '0'
72
58
  requirements: []
73
- rubyforge_project:
74
- rubygems_version: 2.5.1
75
- signing_key:
59
+ rubygems_version: 3.1.2
60
+ signing_key:
76
61
  specification_version: 4
77
62
  summary: Universally Unique Lexicographically Sortable Identifier implementation for
78
63
  Ruby
data/lib/ext/time.rb DELETED
@@ -1,5 +0,0 @@
1
- class Time
2
- def self.now_100usec
3
- (now.to_f * 10_000).to_i
4
- end
5
- end