uxid 0.0.1 → 0.1.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 +4 -4
- data/.rubocop.yml +103 -0
- data/.simplecov +8 -0
- data/CHANGELOG.md +6 -1
- data/Gemfile +7 -1
- data/Gemfile.lock +101 -0
- data/README.md +38 -5
- data/Rakefile +2 -9
- data/bin/console +2 -9
- data/lib/tasks/bundler.rake +4 -0
- data/lib/tasks/check.rake +2 -0
- data/lib/tasks/linting.rake +19 -0
- data/lib/tasks/quality.rake +12 -0
- data/lib/tasks/test.rake +8 -0
- data/lib/uxid.rb +28 -0
- data/lib/uxid/crockford.rb +50 -0
- data/lib/uxid/encoder.rb +211 -0
- data/lib/uxid/model.rb +44 -0
- data/lib/uxid/version.rb +1 -1
- data/uxid.gemspec +7 -6
- metadata +20 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c5ebc6fc8f88263e841c870bb1191ac2769933134e68557bcf8c5472c4a9f3b
|
|
4
|
+
data.tar.gz: 0bea747a87b5ad069325004ea48d9b38e3bd7935e8769f3056e5ceea550b684d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ebd95c6ed10c5bd1272c598bb7d0538fb72888e7e11842ab4aa30cecccb8a92d839d278fba4d5ec62f512df8a5509ac1f93dc7d4102e8262c7b3f0241a4c145a
|
|
7
|
+
data.tar.gz: 6e4bb57c8369218d3015cf81ec967625e33f51cf66e5641b4c63c4b32bba9dad424def094b86ba88464d906176c16d9c187fc4c89cb5caa85234be8d99c9d0f7
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 2.5
|
|
3
|
+
DisabledByDefault: true
|
|
4
|
+
Exclude:
|
|
5
|
+
- pkg/**/*.rb
|
|
6
|
+
- test/spec/**/*.rb
|
|
7
|
+
|
|
8
|
+
Bundler/DuplicatedGem:
|
|
9
|
+
Enabled: true
|
|
10
|
+
Lint:
|
|
11
|
+
Enabled: true
|
|
12
|
+
Security:
|
|
13
|
+
Enabled: true
|
|
14
|
+
|
|
15
|
+
# Single quotes being faster is hardly measurable and only affects parse time.
|
|
16
|
+
# Enforcing double quotes reduces the times where you need to change them
|
|
17
|
+
# when introducing an interpolation. Use single quotes only if their semantics
|
|
18
|
+
# are needed.
|
|
19
|
+
Style/StringLiterals:
|
|
20
|
+
EnforcedStyle: double_quotes
|
|
21
|
+
Exclude:
|
|
22
|
+
- lib/riddler/protobuf/*_pb.rb
|
|
23
|
+
|
|
24
|
+
# Seattle style
|
|
25
|
+
Style/MethodDefParentheses:
|
|
26
|
+
EnforcedStyle: require_no_parentheses
|
|
27
|
+
|
|
28
|
+
# Seattle style
|
|
29
|
+
Style/MethodCallWithoutArgsParentheses:
|
|
30
|
+
Enabled: true
|
|
31
|
+
|
|
32
|
+
# Seattle style
|
|
33
|
+
Style/MethodCallWithArgsParentheses:
|
|
34
|
+
Enabled: true
|
|
35
|
+
EnforcedStyle: omit_parentheses
|
|
36
|
+
AllowParenthesesInChaining: true
|
|
37
|
+
IgnoredMethods:
|
|
38
|
+
- watch
|
|
39
|
+
|
|
40
|
+
Style/SpecialGlobalVars:
|
|
41
|
+
Enabled: false
|
|
42
|
+
|
|
43
|
+
Layout/FirstArrayElementIndentation:
|
|
44
|
+
Enabled: true
|
|
45
|
+
EnforcedStyle: consistent
|
|
46
|
+
|
|
47
|
+
Style/Lambda:
|
|
48
|
+
Enabled: true
|
|
49
|
+
EnforcedStyle: literal
|
|
50
|
+
|
|
51
|
+
# Allow regex argument in Seattle style
|
|
52
|
+
Lint/AmbiguousRegexpLiteral:
|
|
53
|
+
Enabled: false
|
|
54
|
+
|
|
55
|
+
# Allow block argument in Seattle style
|
|
56
|
+
Lint/AmbiguousOperator:
|
|
57
|
+
Enabled: false
|
|
58
|
+
|
|
59
|
+
Layout/SpaceInsideHashLiteralBraces:
|
|
60
|
+
Enabled: true
|
|
61
|
+
EnforcedStyleForEmptyBraces: no_space
|
|
62
|
+
EnforcedStyle: no_space
|
|
63
|
+
|
|
64
|
+
Layout/FirstHashElementIndentation:
|
|
65
|
+
Enabled: true
|
|
66
|
+
EnforcedStyle: consistent
|
|
67
|
+
|
|
68
|
+
Layout/CaseIndentation:
|
|
69
|
+
Enabled: true
|
|
70
|
+
EnforcedStyle: end
|
|
71
|
+
|
|
72
|
+
Layout/EndAlignment:
|
|
73
|
+
Enabled: true
|
|
74
|
+
EnforcedStyleAlignWith: variable
|
|
75
|
+
|
|
76
|
+
Layout/SpaceAroundBlockParameters:
|
|
77
|
+
Enabled: true
|
|
78
|
+
|
|
79
|
+
Layout/SpaceBeforeBlockBraces:
|
|
80
|
+
Enabled: true
|
|
81
|
+
|
|
82
|
+
Layout/SpaceInsideBlockBraces:
|
|
83
|
+
Enabled: true
|
|
84
|
+
|
|
85
|
+
Layout/LineLength:
|
|
86
|
+
Enabled: true
|
|
87
|
+
Max: 120
|
|
88
|
+
Exclude:
|
|
89
|
+
- lib/riddler/protobuf/*_pb.rb
|
|
90
|
+
|
|
91
|
+
Metrics/ClassLength:
|
|
92
|
+
Enabled: true
|
|
93
|
+
Max: 200
|
|
94
|
+
Exclude:
|
|
95
|
+
- test/**/*.rb
|
|
96
|
+
|
|
97
|
+
Lint/SuppressedException:
|
|
98
|
+
Exclude:
|
|
99
|
+
- lib/tasks/**/*.rake
|
|
100
|
+
|
|
101
|
+
Lint/Debugger:
|
|
102
|
+
Exclude:
|
|
103
|
+
- test/**/*
|
data/.simplecov
ADDED
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
|
@@ -3,5 +3,11 @@ source "https://rubygems.org"
|
|
|
3
3
|
# Specify your gem's dependencies in uxid.gemspec
|
|
4
4
|
gemspec
|
|
5
5
|
|
|
6
|
-
gem "rake", "~> 12.0"
|
|
7
6
|
gem "minitest", "~> 5.0"
|
|
7
|
+
gem "minitest-focus"
|
|
8
|
+
gem "minitest-reporters"
|
|
9
|
+
gem "pry-nav"
|
|
10
|
+
gem "rake", "~> 12.0"
|
|
11
|
+
gem "rubycritic", require: false
|
|
12
|
+
gem "rubocop"
|
|
13
|
+
gem "simplecov", require: false
|
data/Gemfile.lock
CHANGED
|
@@ -6,15 +6,116 @@ PATH
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
+
addressable (2.7.0)
|
|
10
|
+
public_suffix (>= 2.0.2, < 5.0)
|
|
11
|
+
ansi (1.5.0)
|
|
12
|
+
ast (2.4.1)
|
|
13
|
+
axiom-types (0.1.1)
|
|
14
|
+
descendants_tracker (~> 0.0.4)
|
|
15
|
+
ice_nine (~> 0.11.0)
|
|
16
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
|
17
|
+
builder (3.2.4)
|
|
18
|
+
coderay (1.1.3)
|
|
19
|
+
coercible (1.0.0)
|
|
20
|
+
descendants_tracker (~> 0.0.1)
|
|
21
|
+
descendants_tracker (0.0.4)
|
|
22
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
|
23
|
+
docile (1.3.2)
|
|
24
|
+
equalizer (0.0.11)
|
|
25
|
+
erubis (2.7.0)
|
|
26
|
+
flay (2.12.1)
|
|
27
|
+
erubis (~> 2.7.0)
|
|
28
|
+
path_expander (~> 1.0)
|
|
29
|
+
ruby_parser (~> 3.0)
|
|
30
|
+
sexp_processor (~> 4.0)
|
|
31
|
+
flog (4.6.4)
|
|
32
|
+
path_expander (~> 1.0)
|
|
33
|
+
ruby_parser (~> 3.1, > 3.1.0)
|
|
34
|
+
sexp_processor (~> 4.8)
|
|
35
|
+
ice_nine (0.11.2)
|
|
36
|
+
kwalify (0.7.2)
|
|
37
|
+
launchy (2.5.0)
|
|
38
|
+
addressable (~> 2.7)
|
|
39
|
+
method_source (0.9.2)
|
|
9
40
|
minitest (5.14.1)
|
|
41
|
+
minitest-focus (1.2.1)
|
|
42
|
+
minitest (>= 4, < 6)
|
|
43
|
+
minitest-reporters (1.4.2)
|
|
44
|
+
ansi
|
|
45
|
+
builder
|
|
46
|
+
minitest (>= 5.0)
|
|
47
|
+
ruby-progressbar
|
|
48
|
+
parallel (1.19.2)
|
|
49
|
+
parser (2.7.1.3)
|
|
50
|
+
ast (~> 2.4.0)
|
|
51
|
+
path_expander (1.1.0)
|
|
52
|
+
pry (0.12.2)
|
|
53
|
+
coderay (~> 1.1.0)
|
|
54
|
+
method_source (~> 0.9.0)
|
|
55
|
+
pry-nav (0.3.0)
|
|
56
|
+
pry (>= 0.9.10, < 0.13.0)
|
|
57
|
+
psych (3.1.0)
|
|
58
|
+
public_suffix (4.0.5)
|
|
59
|
+
rainbow (3.0.0)
|
|
10
60
|
rake (12.3.3)
|
|
61
|
+
reek (6.0.1)
|
|
62
|
+
kwalify (~> 0.7.0)
|
|
63
|
+
parser (>= 2.5.0.0, < 2.8, != 2.5.1.1)
|
|
64
|
+
psych (~> 3.1.0)
|
|
65
|
+
rainbow (>= 2.0, < 4.0)
|
|
66
|
+
regexp_parser (1.7.1)
|
|
67
|
+
rexml (3.2.4)
|
|
68
|
+
rubocop (0.85.1)
|
|
69
|
+
parallel (~> 1.10)
|
|
70
|
+
parser (>= 2.7.0.1)
|
|
71
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
72
|
+
regexp_parser (>= 1.7)
|
|
73
|
+
rexml
|
|
74
|
+
rubocop-ast (>= 0.0.3)
|
|
75
|
+
ruby-progressbar (~> 1.7)
|
|
76
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
|
77
|
+
rubocop-ast (0.0.3)
|
|
78
|
+
parser (>= 2.7.0.1)
|
|
79
|
+
ruby-progressbar (1.10.1)
|
|
80
|
+
ruby_parser (3.14.2)
|
|
81
|
+
sexp_processor (~> 4.9)
|
|
82
|
+
rubycritic (4.5.0)
|
|
83
|
+
flay (~> 2.8)
|
|
84
|
+
flog (~> 4.4)
|
|
85
|
+
launchy (>= 2.0.0)
|
|
86
|
+
parser (>= 2.6.0)
|
|
87
|
+
rainbow (~> 3.0)
|
|
88
|
+
reek (~> 6.0, < 7.0)
|
|
89
|
+
ruby_parser (~> 3.8)
|
|
90
|
+
simplecov (>= 0.17.0)
|
|
91
|
+
tty-which (~> 0.4.0)
|
|
92
|
+
virtus (~> 1.0)
|
|
93
|
+
sexp_processor (4.15.0)
|
|
94
|
+
simplecov (0.18.5)
|
|
95
|
+
docile (~> 1.1)
|
|
96
|
+
simplecov-html (~> 0.11)
|
|
97
|
+
simplecov-html (0.12.2)
|
|
98
|
+
thread_safe (0.3.6)
|
|
99
|
+
tty-which (0.4.2)
|
|
100
|
+
unicode-display_width (1.7.0)
|
|
101
|
+
virtus (1.0.5)
|
|
102
|
+
axiom-types (~> 0.1)
|
|
103
|
+
coercible (~> 1.0)
|
|
104
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
|
105
|
+
equalizer (~> 0.0, >= 0.0.9)
|
|
11
106
|
|
|
12
107
|
PLATFORMS
|
|
13
108
|
ruby
|
|
14
109
|
|
|
15
110
|
DEPENDENCIES
|
|
16
111
|
minitest (~> 5.0)
|
|
112
|
+
minitest-focus
|
|
113
|
+
minitest-reporters
|
|
114
|
+
pry-nav
|
|
17
115
|
rake (~> 12.0)
|
|
116
|
+
rubocop
|
|
117
|
+
rubycritic
|
|
118
|
+
simplecov
|
|
18
119
|
uxid!
|
|
19
120
|
|
|
20
121
|
BUNDLED WITH
|
data/README.md
CHANGED
|
@@ -1,8 +1,37 @@
|
|
|
1
1
|
# UXID
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
![MIT License][badge_license_url]
|
|
4
|
+
![Gem Version][badge_version_url]
|
|
5
|
+
|
|
6
|
+
UXIDs are identifiers which:
|
|
7
|
+
|
|
8
|
+
* Describe the resource (aid in debugging and investigation)
|
|
9
|
+
* Prononounceable - easily and accurately transmit characters to another human using a telephone
|
|
10
|
+
* Work well with copy and paste (double clicking selects the entire ID)
|
|
11
|
+
* Can be shortened for low cardinality resources
|
|
12
|
+
* Are very unlikely to collide (more likely with less randomness)
|
|
13
|
+
* Are secure against enumeration attacks
|
|
14
|
+
* Can be generated by application code (not tied to the datastore)
|
|
15
|
+
* Are K-sortable (lexicographically sortable by time - works well with datastore indexing)
|
|
16
|
+
* Do not require any coordination (human or automated) at startup, or generation
|
|
17
|
+
|
|
18
|
+
Many of the concepts of Stripe IDs have been used in this library.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
#### Generating UXIDs
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# No options generates a basic ULID
|
|
26
|
+
UXID.generate # "01EMDGJF0DQXQJ8FM78XE97Y3H"
|
|
27
|
+
|
|
28
|
+
# A prefix can be provided
|
|
29
|
+
UXID.generate prefix: "cus" # "cus_01EMDGJF0DQXQJ8FM78XE97Y3H"
|
|
30
|
+
|
|
31
|
+
# The amount of entropy can be decreased for smaller cardinality resources
|
|
32
|
+
UXID.generate prefix: "cus", size: 4 # "cus_01EMDGN5QAGPWSKN2"
|
|
33
|
+
```
|
|
4
34
|
|
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
|
6
35
|
|
|
7
36
|
## Installation
|
|
8
37
|
|
|
@@ -20,9 +49,6 @@ Or install it yourself as:
|
|
|
20
49
|
|
|
21
50
|
$ gem install uxid
|
|
22
51
|
|
|
23
|
-
## Usage
|
|
24
|
-
|
|
25
|
-
TODO: Write usage instructions here
|
|
26
52
|
|
|
27
53
|
## Development
|
|
28
54
|
|
|
@@ -38,3 +64,10 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/riddle
|
|
|
38
64
|
## License
|
|
39
65
|
|
|
40
66
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
67
|
+
|
|
68
|
+
<!-- LINKS -->
|
|
69
|
+
[mit_license_url]: http://opensource.org/licenses/MIT
|
|
70
|
+
|
|
71
|
+
<!-- BADGES -->
|
|
72
|
+
[badge_license_url]: https://img.shields.io/badge/license-MIT-brightgreen.svg?cacheSeconds=3600?style=flat-square
|
|
73
|
+
[badge_version_url]: https://badge.fury.io/rb/uxid.svg
|
data/Rakefile
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
require "rake/testtask"
|
|
1
|
+
Rake.add_rakelib "lib/tasks"
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
t.libs << "test"
|
|
6
|
-
t.libs << "lib"
|
|
7
|
-
t.test_files = FileList["test/**/*_test.rb"]
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
task :default => :test
|
|
3
|
+
task default: :test
|
data/bin/console
CHANGED
|
@@ -3,12 +3,5 @@
|
|
|
3
3
|
require "bundler/setup"
|
|
4
4
|
require "uxid"
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
-
# require "pry"
|
|
11
|
-
# Pry.start
|
|
12
|
-
|
|
13
|
-
require "irb"
|
|
14
|
-
IRB.start(__FILE__)
|
|
6
|
+
require "pry"
|
|
7
|
+
Pry.start
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require "rubocop/rake_task"
|
|
3
|
+
|
|
4
|
+
desc "Run Linting (RuboCop)"
|
|
5
|
+
task linting: "linting:dev"
|
|
6
|
+
|
|
7
|
+
namespace :linting do
|
|
8
|
+
common_options = %w[ --display-cop-names --extra-details --config=.rubocop.yml --parallel ]
|
|
9
|
+
|
|
10
|
+
RuboCop::RakeTask.new :dev do |t|
|
|
11
|
+
t.options = common_options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
RuboCop::RakeTask.new :report do |t|
|
|
15
|
+
t.options = common_options + %w[ --format=html --out=reports/linting/index.html ]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
rescue LoadError
|
|
19
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require "rubycritic/rake_task"
|
|
3
|
+
|
|
4
|
+
namespace :metrics do
|
|
5
|
+
RubyCritic::RakeTask.new do |task|
|
|
6
|
+
task.name = "quality"
|
|
7
|
+
task.paths = FileList["lib/**/*.rb"]
|
|
8
|
+
task.options = "--format html --no-browser --path reports/quality"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
rescue LoadError
|
|
12
|
+
end
|
data/lib/tasks/test.rake
ADDED
data/lib/uxid.rb
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
if RUBY_VERSION >= "2.5"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
else
|
|
6
|
+
require "sysrandom/securerandom"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require "uxid/model"
|
|
10
|
+
require "uxid/encoder"
|
|
1
11
|
require "uxid/version"
|
|
2
12
|
|
|
3
13
|
module UXID
|
|
14
|
+
CROCKFORD_ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
|
15
|
+
DELIMITER = "_"
|
|
16
|
+
|
|
4
17
|
class Error < StandardError; end
|
|
18
|
+
|
|
19
|
+
def self.generate attrs = {}
|
|
20
|
+
model = new attrs
|
|
21
|
+
model.encode
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.new attrs = {}
|
|
25
|
+
model = ::UXID::Model.new
|
|
26
|
+
|
|
27
|
+
model.time = attrs[:time] || Time.now
|
|
28
|
+
model.size = attrs[:size] || 10
|
|
29
|
+
model.prefix = attrs[:prefix] || ""
|
|
30
|
+
|
|
31
|
+
model
|
|
32
|
+
end
|
|
5
33
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module UXID
|
|
2
|
+
module Crockford
|
|
3
|
+
ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ".freeze
|
|
4
|
+
|
|
5
|
+
class Chunk
|
|
6
|
+
def initialize bytes
|
|
7
|
+
@bytes = bytes
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def decode
|
|
11
|
+
bytes = @bytes.take_while {|c| c != 61} # strip padding
|
|
12
|
+
n = (bytes.length * 5.0 / 8.0).floor
|
|
13
|
+
p = bytes.length < 8 ? 5 - (n * 8) % 5 : 0
|
|
14
|
+
c = bytes.inject(0) do |m,o|
|
|
15
|
+
i = ALPHABET.index o.chr
|
|
16
|
+
raise ArgumentError, "invalid character '#{o.chr}'" if i.nil?
|
|
17
|
+
(m << 5) + i
|
|
18
|
+
end >> p
|
|
19
|
+
(0..n-1).to_a.reverse.collect {|i| ((c >> i * 8) & 0xff).chr}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def encode
|
|
23
|
+
n = (@bytes.length * 8.0 / 5.0).ceil
|
|
24
|
+
p = n < 8 ? 5 - (@bytes.length * 8) % 5 : 0
|
|
25
|
+
c = @bytes.inject(0) {|m,o| (m << 8) + o} << p
|
|
26
|
+
[(0..n-1).to_a.reverse.collect {|i| ALPHABET[(c >> i * 5) & 0x1f].chr}]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.chunks str, size
|
|
31
|
+
result = []
|
|
32
|
+
bytes = str.bytes
|
|
33
|
+
while bytes.any? do
|
|
34
|
+
chunk_bytes = bytes.take size
|
|
35
|
+
bytes = bytes.drop size
|
|
36
|
+
new_chunk = Chunk.new chunk_bytes
|
|
37
|
+
result << new_chunk
|
|
38
|
+
end
|
|
39
|
+
result
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.encode str
|
|
43
|
+
chunks(str, 5).collect(&:encode).flatten.join
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.decode str
|
|
47
|
+
chunks(str, 8).collect(&:decode).flatten.join
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/uxid/encoder.rb
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
module UXID
|
|
2
|
+
class Encoder
|
|
3
|
+
def self.encode model
|
|
4
|
+
new(model).encode
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def initialize model
|
|
8
|
+
@model = model
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def encode
|
|
12
|
+
@model.time_encoded = encode_time
|
|
13
|
+
@model.entropy_encoded = public_send "encode_entropy_#{@model.size}"
|
|
14
|
+
|
|
15
|
+
@model.encoded
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def encode_entropy_0; ""; end
|
|
19
|
+
|
|
20
|
+
def encode_entropy_1
|
|
21
|
+
b0, _b1 = @model.entropy_bytes
|
|
22
|
+
|
|
23
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
24
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def encode_entropy_2
|
|
28
|
+
b0, b1 = @model.entropy_bytes
|
|
29
|
+
|
|
30
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
31
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
32
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
33
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
34
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
35
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4)]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def encode_entropy_2
|
|
39
|
+
b0, b1 = @model.entropy_bytes
|
|
40
|
+
|
|
41
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
42
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
43
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
44
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
45
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
46
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4)]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def encode_entropy_3
|
|
50
|
+
b0, b1, b2 = @model.entropy_bytes
|
|
51
|
+
|
|
52
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
53
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
54
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
55
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
56
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
57
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4) | ((b2&240)>>4)] +
|
|
58
|
+
UXID::CROCKFORD_ENCODING[((b2&15)<<1)]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def encode_entropy_4
|
|
62
|
+
b0, b1, b2, b3 = @model.entropy_bytes
|
|
63
|
+
|
|
64
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
65
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
66
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
67
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
68
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
69
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4) | ((b2&240)>>4)] +
|
|
70
|
+
UXID::CROCKFORD_ENCODING[((b2&15)<<1) | ((b3&128)>>7)] +
|
|
71
|
+
UXID::CROCKFORD_ENCODING[(b3&124)>>2] +
|
|
72
|
+
UXID::CROCKFORD_ENCODING[((b3&3)<<3)]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def encode_entropy_5
|
|
76
|
+
b0, b1, b2, b3, b4 = @model.entropy_bytes
|
|
77
|
+
|
|
78
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
79
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
80
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
81
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
82
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
83
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4) | ((b2&240)>>4)] +
|
|
84
|
+
UXID::CROCKFORD_ENCODING[((b2&15)<<1) | ((b3&128)>>7)] +
|
|
85
|
+
UXID::CROCKFORD_ENCODING[(b3&124)>>2] +
|
|
86
|
+
UXID::CROCKFORD_ENCODING[((b3&3)<<3) | ((b4&224)>>5)] +
|
|
87
|
+
UXID::CROCKFORD_ENCODING[b4&31]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def encode_entropy_6
|
|
91
|
+
b0, b1, b2, b3, b4, b5 = @model.entropy_bytes
|
|
92
|
+
|
|
93
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
94
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
95
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
96
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
97
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
98
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4) | ((b2&240)>>4)] +
|
|
99
|
+
UXID::CROCKFORD_ENCODING[((b2&15)<<1) | ((b3&128)>>7)] +
|
|
100
|
+
UXID::CROCKFORD_ENCODING[(b3&124)>>2] +
|
|
101
|
+
UXID::CROCKFORD_ENCODING[((b3&3)<<3) | ((b4&224)>>5)] +
|
|
102
|
+
UXID::CROCKFORD_ENCODING[b4&31] +
|
|
103
|
+
UXID::CROCKFORD_ENCODING[(b5&248)>>3] +
|
|
104
|
+
UXID::CROCKFORD_ENCODING[((b5&7)<<2)]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def encode_entropy_7
|
|
108
|
+
b0, b1, b2, b3, b4, b5, b6 = @model.entropy_bytes
|
|
109
|
+
|
|
110
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
111
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
112
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
113
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
114
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
115
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4) | ((b2&240)>>4)] +
|
|
116
|
+
UXID::CROCKFORD_ENCODING[((b2&15)<<1) | ((b3&128)>>7)] +
|
|
117
|
+
UXID::CROCKFORD_ENCODING[(b3&124)>>2] +
|
|
118
|
+
UXID::CROCKFORD_ENCODING[((b3&3)<<3) | ((b4&224)>>5)] +
|
|
119
|
+
UXID::CROCKFORD_ENCODING[b4&31] +
|
|
120
|
+
UXID::CROCKFORD_ENCODING[(b5&248)>>3] +
|
|
121
|
+
UXID::CROCKFORD_ENCODING[((b5&7)<<2) | ((b6&192)>>6)] +
|
|
122
|
+
UXID::CROCKFORD_ENCODING[(b6&62)>>1] +
|
|
123
|
+
UXID::CROCKFORD_ENCODING[((b6&1)<<4)]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def encode_entropy_8
|
|
127
|
+
b0, b1, b2, b3, b4, b5, b6, b7 = @model.entropy_bytes
|
|
128
|
+
|
|
129
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
130
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
131
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
132
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
133
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
134
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4) | ((b2&240)>>4)] +
|
|
135
|
+
UXID::CROCKFORD_ENCODING[((b2&15)<<1) | ((b3&128)>>7)] +
|
|
136
|
+
UXID::CROCKFORD_ENCODING[(b3&124)>>2] +
|
|
137
|
+
UXID::CROCKFORD_ENCODING[((b3&3)<<3) | ((b4&224)>>5)] +
|
|
138
|
+
UXID::CROCKFORD_ENCODING[b4&31] +
|
|
139
|
+
UXID::CROCKFORD_ENCODING[(b5&248)>>3] +
|
|
140
|
+
UXID::CROCKFORD_ENCODING[((b5&7)<<2) | ((b6&192)>>6)] +
|
|
141
|
+
UXID::CROCKFORD_ENCODING[(b6&62)>>1] +
|
|
142
|
+
UXID::CROCKFORD_ENCODING[((b6&1)<<4) | ((b7&240)>>4)] +
|
|
143
|
+
UXID::CROCKFORD_ENCODING[((b7&15)<<1)]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def encode_entropy_9
|
|
147
|
+
b0, b1, b2, b3, b4, b5, b6, b7, b8 = @model.entropy_bytes
|
|
148
|
+
|
|
149
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
150
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
151
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
152
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
153
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
154
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4) | ((b2&240)>>4)] +
|
|
155
|
+
UXID::CROCKFORD_ENCODING[((b2&15)<<1) | ((b3&128)>>7)] +
|
|
156
|
+
UXID::CROCKFORD_ENCODING[(b3&124)>>2] +
|
|
157
|
+
UXID::CROCKFORD_ENCODING[((b3&3)<<3) | ((b4&224)>>5)] +
|
|
158
|
+
UXID::CROCKFORD_ENCODING[b4&31] +
|
|
159
|
+
UXID::CROCKFORD_ENCODING[(b5&248)>>3] +
|
|
160
|
+
UXID::CROCKFORD_ENCODING[((b5&7)<<2) | ((b6&192)>>6)] +
|
|
161
|
+
UXID::CROCKFORD_ENCODING[(b6&62)>>1] +
|
|
162
|
+
UXID::CROCKFORD_ENCODING[((b6&1)<<4) | ((b7&240)>>4)] +
|
|
163
|
+
UXID::CROCKFORD_ENCODING[((b7&15)<<1)] +
|
|
164
|
+
UXID::CROCKFORD_ENCODING[((b7&15)<<1) | ((b8&128)>>7)] +
|
|
165
|
+
UXID::CROCKFORD_ENCODING[(b8&124)>>2] +
|
|
166
|
+
UXID::CROCKFORD_ENCODING[((b8&3)<<3)]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def encode_entropy_10
|
|
170
|
+
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = @model.entropy_bytes
|
|
171
|
+
|
|
172
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
173
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2)] +
|
|
174
|
+
UXID::CROCKFORD_ENCODING[(b0&248)>>3] +
|
|
175
|
+
UXID::CROCKFORD_ENCODING[((b0&7)<<2) | ((b1&192)>>6)] +
|
|
176
|
+
UXID::CROCKFORD_ENCODING[(b1&62)>>1] +
|
|
177
|
+
UXID::CROCKFORD_ENCODING[((b1&1)<<4) | ((b2&240)>>4)] +
|
|
178
|
+
UXID::CROCKFORD_ENCODING[((b2&15)<<1) | ((b3&128)>>7)] +
|
|
179
|
+
UXID::CROCKFORD_ENCODING[(b3&124)>>2] +
|
|
180
|
+
UXID::CROCKFORD_ENCODING[((b3&3)<<3) | ((b4&224)>>5)] +
|
|
181
|
+
UXID::CROCKFORD_ENCODING[b4&31] +
|
|
182
|
+
UXID::CROCKFORD_ENCODING[(b5&248)>>3] +
|
|
183
|
+
UXID::CROCKFORD_ENCODING[((b5&7)<<2) | ((b6&192)>>6)] +
|
|
184
|
+
UXID::CROCKFORD_ENCODING[(b6&62)>>1] +
|
|
185
|
+
UXID::CROCKFORD_ENCODING[((b6&1)<<4) | ((b7&240)>>4)] +
|
|
186
|
+
UXID::CROCKFORD_ENCODING[((b7&15)<<1)] +
|
|
187
|
+
UXID::CROCKFORD_ENCODING[((b7&15)<<1) | ((b8&128)>>7)] +
|
|
188
|
+
UXID::CROCKFORD_ENCODING[(b8&124)>>2] +
|
|
189
|
+
UXID::CROCKFORD_ENCODING[((b8&3)<<3) | ((b9&224)>>5)] +
|
|
190
|
+
UXID::CROCKFORD_ENCODING[b9&31]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def encode_time
|
|
194
|
+
# Get the 5 bytes of the 48bit time
|
|
195
|
+
b0, b1, b2, b3, b4, b5 = @model.time_bytes
|
|
196
|
+
|
|
197
|
+
# Split these 5 bytes up into 10 characters using the encoding
|
|
198
|
+
# and join them together as a string
|
|
199
|
+
UXID::CROCKFORD_ENCODING[(b0&224)>>5] +
|
|
200
|
+
UXID::CROCKFORD_ENCODING[b0&31] +
|
|
201
|
+
UXID::CROCKFORD_ENCODING[(b1&248)>>3] +
|
|
202
|
+
UXID::CROCKFORD_ENCODING[((b1&7)<<2) | ((b2&192)>>6)] +
|
|
203
|
+
UXID::CROCKFORD_ENCODING[(b2&62)>>1] +
|
|
204
|
+
UXID::CROCKFORD_ENCODING[((b2&1)<<4) | ((b3&240)>>4)] +
|
|
205
|
+
UXID::CROCKFORD_ENCODING[((b3&15)<<1) | ((b4&128)>>7)] +
|
|
206
|
+
UXID::CROCKFORD_ENCODING[(b4&124)>>2] +
|
|
207
|
+
UXID::CROCKFORD_ENCODING[((b4&3)<<3) | ((b5&224)>>5)] +
|
|
208
|
+
UXID::CROCKFORD_ENCODING[b5&31]
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
data/lib/uxid/model.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "uxid/encoder"
|
|
2
|
+
|
|
3
|
+
module UXID
|
|
4
|
+
class Model
|
|
5
|
+
attr_accessor :time, :time_encoded, :entropy, :entropy_encoded, :prefix, :size
|
|
6
|
+
|
|
7
|
+
def encode
|
|
8
|
+
encoder = ::UXID::Encoder.new self
|
|
9
|
+
encoder.encode
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Returns time as 48 bits
|
|
13
|
+
def time_bytes
|
|
14
|
+
return @time_bytes if @time_bytes
|
|
15
|
+
|
|
16
|
+
time_ms = (@time.to_f * 1000).to_i
|
|
17
|
+
bytes = [time_ms].pack "Q>"
|
|
18
|
+
bytes_string = bytes[2..-1]
|
|
19
|
+
@time_bytes = bytes_string.bytes
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def entropy_bytes
|
|
23
|
+
return @entropy_bytes if @entropy_bytes
|
|
24
|
+
|
|
25
|
+
bytes_string = SecureRandom.random_bytes @size
|
|
26
|
+
@entropy_bytes = bytes_string.bytes
|
|
27
|
+
return @entropy_bytes
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def encoded
|
|
31
|
+
id = time_encoded + entropy_encoded
|
|
32
|
+
|
|
33
|
+
if has_prefix?
|
|
34
|
+
[prefix, id].join UXID::DELIMITER
|
|
35
|
+
else
|
|
36
|
+
id
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def has_prefix?
|
|
41
|
+
!(prefix.nil? || prefix == "")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/uxid/version.rb
CHANGED
data/uxid.gemspec
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "lib/uxid/version"
|
|
2
|
+
here = File.expand_path "..", __FILE__
|
|
2
3
|
|
|
3
4
|
Gem::Specification.new do |spec|
|
|
4
5
|
spec.name = "uxid"
|
|
@@ -6,21 +7,21 @@ Gem::Specification.new do |spec|
|
|
|
6
7
|
spec.authors = ["JohnnyT"]
|
|
7
8
|
spec.email = ["ubergeek3141@gmail.com"]
|
|
8
9
|
|
|
9
|
-
spec.summary = "
|
|
10
|
+
spec.summary = "Generates IDs like: cus_01EPEY1JMKXVBT and txn_01EPEY2P06TR1RTV07XA82ZGJJ (similar to Stripe identifiers)."
|
|
10
11
|
spec.description = spec.summary
|
|
11
12
|
spec.homepage = "https://github.com/riddler/uxid"
|
|
12
13
|
spec.license = "MIT"
|
|
13
|
-
spec.required_ruby_version = Gem::Requirement.new
|
|
14
|
+
spec.required_ruby_version = Gem::Requirement.new ">= 2.3.0"
|
|
14
15
|
|
|
15
16
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
16
17
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
17
18
|
|
|
18
19
|
# Specify which files should be added to the gem when it is released.
|
|
19
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
20
|
-
spec.files = Dir.chdir
|
|
21
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match
|
|
21
|
+
spec.files = Dir.chdir here do
|
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match %r{^(test|spec|features)/} }
|
|
22
23
|
end
|
|
23
24
|
spec.bindir = "exe"
|
|
24
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename
|
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename f }
|
|
25
26
|
spec.require_paths = ["lib"]
|
|
26
27
|
end
|
metadata
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: uxid
|
|
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
|
- JohnnyT
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
11
|
+
date: 2020-11-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
|
-
description:
|
|
13
|
+
description: 'Generates IDs like: cus_01EPEY1JMKXVBT and txn_01EPEY2P06TR1RTV07XA82ZGJJ
|
|
14
|
+
(similar to Stripe identifiers).'
|
|
14
15
|
email:
|
|
15
16
|
- ubergeek3141@gmail.com
|
|
16
17
|
executables: []
|
|
17
18
|
extensions: []
|
|
18
19
|
extra_rdoc_files: []
|
|
19
20
|
files:
|
|
21
|
+
- ".rubocop.yml"
|
|
22
|
+
- ".simplecov"
|
|
20
23
|
- CHANGELOG.md
|
|
21
24
|
- Gemfile
|
|
22
25
|
- Gemfile.lock
|
|
@@ -25,7 +28,15 @@ files:
|
|
|
25
28
|
- Rakefile
|
|
26
29
|
- bin/console
|
|
27
30
|
- bin/setup
|
|
31
|
+
- lib/tasks/bundler.rake
|
|
32
|
+
- lib/tasks/check.rake
|
|
33
|
+
- lib/tasks/linting.rake
|
|
34
|
+
- lib/tasks/quality.rake
|
|
35
|
+
- lib/tasks/test.rake
|
|
28
36
|
- lib/uxid.rb
|
|
37
|
+
- lib/uxid/crockford.rb
|
|
38
|
+
- lib/uxid/encoder.rb
|
|
39
|
+
- lib/uxid/model.rb
|
|
29
40
|
- lib/uxid/version.rb
|
|
30
41
|
- uxid.gemspec
|
|
31
42
|
homepage: https://github.com/riddler/uxid
|
|
@@ -34,7 +45,7 @@ licenses:
|
|
|
34
45
|
metadata:
|
|
35
46
|
homepage_uri: https://github.com/riddler/uxid
|
|
36
47
|
source_code_uri: https://github.com/riddler/uxid
|
|
37
|
-
post_install_message:
|
|
48
|
+
post_install_message:
|
|
38
49
|
rdoc_options: []
|
|
39
50
|
require_paths:
|
|
40
51
|
- lib
|
|
@@ -49,8 +60,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
49
60
|
- !ruby/object:Gem::Version
|
|
50
61
|
version: '0'
|
|
51
62
|
requirements: []
|
|
52
|
-
rubygems_version: 3.1.
|
|
53
|
-
signing_key:
|
|
63
|
+
rubygems_version: 3.1.4
|
|
64
|
+
signing_key:
|
|
54
65
|
specification_version: 4
|
|
55
|
-
summary:
|
|
66
|
+
summary: 'Generates IDs like: cus_01EPEY1JMKXVBT and txn_01EPEY2P06TR1RTV07XA82ZGJJ
|
|
67
|
+
(similar to Stripe identifiers).'
|
|
56
68
|
test_files: []
|