ulid 0.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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