ksuid 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +56 -10
- data/UPGRADING.md +11 -0
- data/ksuid.gemspec +3 -1
- data/lib/{ksuid/activerecord → active_record/ksuid}/binary_type.rb +9 -9
- data/lib/active_record/ksuid/prefixed_type.rb +70 -0
- data/lib/active_record/ksuid/railtie.rb +27 -0
- data/lib/{ksuid/activerecord → active_record/ksuid}/table_definition.rb +6 -5
- data/lib/{ksuid/activerecord → active_record/ksuid}/type.rb +9 -9
- data/lib/active_record/ksuid.rb +119 -0
- data/lib/ksuid/activerecord.rb +11 -49
- data/lib/ksuid/base62.rb +4 -4
- data/lib/ksuid/configuration.rb +4 -1
- data/lib/ksuid/prefixed.rb +242 -0
- data/lib/ksuid/type.rb +0 -3
- data/lib/ksuid/utils.rb +1 -1
- data/lib/ksuid/version.rb +1 -1
- data/lib/ksuid.rb +48 -3
- metadata +12 -7
- data/lib/ksuid/railtie.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a25a1c0246464a41b5b419ad0fffbd8c5b08b7dfb882ccb31a29a0a6fbbc0fe1
|
4
|
+
data.tar.gz: 70a978445c93cc062cb704a5156fd919c623677fc1666723ba1f828067978fcd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ed9fde240ebee2cdffcb81877548b780286844d2ccfa6a4200f4a0c36161e1d53f073b761e22d7b3fb0d857a31b2fae50fdb30a6ca3571b1cc933ea09281363
|
7
|
+
data.tar.gz: 946ad6b51f84fca3b1a0230c4892fc3afd862baabe232efac3b0f9d196d16e2c16702e645faf12bcb9e2614c15a504f007c9cc44383c5ac6e87b8feeb5554d7f
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## [0.5.0](https://github.com/michaelherold/ksuid/compare/v0.4.0...v0.5.0) - 2022-08-18
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- If you'd rather deal in KSUID strings instead of `KSUID::Type`s, you can now generate them simply with `KSUID.string`. It takes the same arguments, `payload` and `time` as `KSUID.new`, but returns a string instead of a `KSUID::Type`.
|
12
|
+
- `KSUID.prefixed` and the `KSUID::Prefixed` class now can generate prefixed KSUIDs to make them visually identifiable for their source. You cannot prefix a binary-encoded KSUID, only base 62-encoded ones.
|
13
|
+
- `ActiveRecord::KSUID` now accepts a `prefix:` argument for handling prefixed KSUIDs. In addition, the `ksuid` column type also accepts a `prefix:` argument to calculate the intended size of the column with the prefix.
|
14
|
+
|
15
|
+
### Deprecated
|
16
|
+
|
17
|
+
- `KSUID::ActiveRecord` is now `ActiveRecord::KSUID`. The original constant will continue to work until v1.0.0, but will emit a warning upon boot of your application. To silence the deprecation, change all uses of `KSUID::ActiveRecord` to the new constant, `ActiveRecord::KSUID`. See the [upgrading notice][./UPGRADING.md] for more information.
|
18
|
+
|
19
|
+
### Miscellaneous
|
20
|
+
|
21
|
+
- The compatibility check for the Base62 implementation in the gem is about 10x faster now. The original optimization did not optimize as much due to an error with the benchmark. This change has a tested benchmark that shows a great improvement. Note that this is a micro-optimization and we see no real performance gain in the parsing of KSUID strings.
|
22
|
+
|
7
23
|
## [0.4.0](https://github.com/michaelherold/ksuid/compare/v0.3.0...v0.4.0) - 2022-07-29
|
8
24
|
|
9
25
|
### Added
|
data/README.md
CHANGED
@@ -100,10 +100,34 @@ KSUID.configure do |config|
|
|
100
100
|
end
|
101
101
|
```
|
102
102
|
|
103
|
+
### Prefixed KSUIDs
|
104
|
+
|
105
|
+
If you use KSUIDs in multiple contexts, you can prefix them to make them easily identifiable.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
ksuid = KSUID.prefixed('evt_')
|
109
|
+
```
|
110
|
+
|
111
|
+
Just like a normal KSUID, you can use a specific timestamp:
|
112
|
+
|
113
|
+
``` ruby
|
114
|
+
ksuid = KSUID.prefixed('evt_', time: time) # where time is a Time-like object
|
115
|
+
```
|
116
|
+
|
117
|
+
You can also parse a prefixed KSUID from a string that you received:
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
ksuid = KSUID::Prefixed.from_base62(base62_string, prefix: 'evt_')
|
121
|
+
```
|
122
|
+
|
123
|
+
Prefixed KSUIDs order themselves with non-prefixed KSUIDs as if their prefix did not exist. With other prefixed KSUIDs, they order first by their prefix, then their timestamp.
|
124
|
+
|
103
125
|
### ActiveRecord
|
104
126
|
|
105
127
|
Whether you are using ActiveRecord inside an existing project or in a new project, usage is simple. Additionally, you can use it with or without Rails.
|
106
128
|
|
129
|
+
_Note: In v1.0.0 of KSUID for Ruby, we will extract this behavior into a separate gem, `activerecord-ksuid`. It will be API-compatible with the implementation in v0.5.0+ of KSUID for Ruby. There will be upgrade instructions for v1.0.0 once we release it._
|
130
|
+
|
107
131
|
#### Adding to an existing model
|
108
132
|
|
109
133
|
Within a Rails project, it is very easy to get started using KSUIDs within your models. You can use the `ksuid` column type in a Rails migration to add a column to an existing model:
|
@@ -124,7 +148,7 @@ Then, to add proper handling to the field, you will want to mix a module into th
|
|
124
148
|
|
125
149
|
```ruby
|
126
150
|
class Event < ApplicationRecord
|
127
|
-
include KSUID
|
151
|
+
include ActiveRecord::KSUID[:unique_id]
|
128
152
|
end
|
129
153
|
```
|
130
154
|
|
@@ -148,7 +172,7 @@ Once you have generated the table that you will use for your model, you will nee
|
|
148
172
|
|
149
173
|
```ruby
|
150
174
|
class Event < ApplicationRecord
|
151
|
-
include KSUID
|
175
|
+
include ActiveRecord::KSUID[:my_field_name]
|
152
176
|
end
|
153
177
|
```
|
154
178
|
|
@@ -168,7 +192,7 @@ You will need to mix in the module into your model as well:
|
|
168
192
|
|
169
193
|
```ruby
|
170
194
|
class Event < ApplicationRecord
|
171
|
-
include KSUID
|
195
|
+
include ActiveRecord::KSUID[:id]
|
172
196
|
end
|
173
197
|
```
|
174
198
|
|
@@ -177,10 +201,10 @@ end
|
|
177
201
|
Outside of Rails, you cannot rely on the Railtie to load the appropriate files for you automatically. Toward the start of your application's boot process, you will want to require the following:
|
178
202
|
|
179
203
|
```ruby
|
180
|
-
require 'ksuid
|
204
|
+
require 'active_record/ksuid'
|
181
205
|
|
182
206
|
# If you will be using the ksuid column type in a migration
|
183
|
-
require 'ksuid/
|
207
|
+
require 'active_record/ksuid/table_definition'
|
184
208
|
```
|
185
209
|
|
186
210
|
Once you have required the file(s) that you need, everything else will work as it does above.
|
@@ -193,17 +217,39 @@ When you include the KSUID module into your model, you will want to pass the `:b
|
|
193
217
|
|
194
218
|
```ruby
|
195
219
|
class Event < ApplicationRecord
|
196
|
-
include KSUID
|
220
|
+
include ActiveRecord::KSUID[:my_field_name, binary: true]
|
197
221
|
end
|
198
222
|
```
|
199
223
|
|
224
|
+
#### Using a prefix on your KSUID field
|
225
|
+
|
226
|
+
For prefixed KSUIDs in ActiveRecord, you must pass the intended prefix during table definition so that the field is of appropriate size.
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
class CreateEvents < ActiveRecord::Migration[5.2]
|
230
|
+
create_table :events do |table|
|
231
|
+
table.ksuid :ksuid, prefix: 'evt_'
|
232
|
+
end
|
233
|
+
end
|
234
|
+
```
|
235
|
+
|
236
|
+
You also must pass it in the module builder that you include in your model:
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
class Event < ApplicationRecord
|
240
|
+
include ActiveRecord::KSUID[:ksuid, prefix: 'evt_']
|
241
|
+
end
|
242
|
+
```
|
243
|
+
|
244
|
+
You cannot use a prefix with a binary-encoded KSUID.
|
245
|
+
|
200
246
|
#### Use the KSUID as your `created_at` timestamp
|
201
247
|
|
202
248
|
Since KSUIDs include a timestamp as well, you can infer the `#created_at` timestamp from the KSUID. The module builder enables that option automatically with the `:created_at` option, like so:
|
203
249
|
|
204
250
|
```ruby
|
205
251
|
class Event < ApplicationRecord
|
206
|
-
include KSUID
|
252
|
+
include ActiveRecord::KSUID[:my_field_name, created_at: true]
|
207
253
|
end
|
208
254
|
```
|
209
255
|
|
@@ -217,10 +263,10 @@ So you’re interested in contributing to KSUID? Check out our [contributing gui
|
|
217
263
|
|
218
264
|
This library aims to support and is [tested against][actions] the following Ruby versions:
|
219
265
|
|
220
|
-
* Ruby 2.5
|
221
|
-
* Ruby 2.6
|
222
266
|
* Ruby 2.7
|
223
|
-
*
|
267
|
+
* Ruby 3.0
|
268
|
+
* Ruby 3.1
|
269
|
+
* JRuby 9.3
|
224
270
|
|
225
271
|
If something doesn't work on one of these versions, it's a bug.
|
226
272
|
|
data/UPGRADING.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Upgrading instructions for KSUID for Ruby
|
2
|
+
|
3
|
+
## v0.5.0
|
4
|
+
|
5
|
+
### Deprecated `KSUID::ActiveRecord` in favor of `ActiveRecord::KSUID`
|
6
|
+
|
7
|
+
This version deprecates the original constant for the ActiveRecord integration, `KSUID::ActiveRecord`. This change is in preparation for extracting the ActiveRecord integration into its own gem. Continuing to use the original constant will show deprecation warnings upon boot of your application.
|
8
|
+
|
9
|
+
Migrating for this version should be quick: simply do a global replace of `KSUID::ActiveRecord` for `ActiveRecord::KSUID`. No other changes should be necessary.
|
10
|
+
|
11
|
+
In the future release of v1.0.0, you will need to also include `activerecord-ksuid` your Gemfile. This gem is as-yet unreleased, with a release intended concurrently with v1.0.0 of KSUID for Ruby.
|
data/ksuid.gemspec
CHANGED
@@ -13,10 +13,12 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.homepage = 'https://github.com/michaelherold/ksuid-ruby'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
|
-
spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md]
|
16
|
+
spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md UPGRADING.md]
|
17
17
|
spec.files += %w[ksuid.gemspec]
|
18
18
|
spec.files += Dir['lib/**/*.rb']
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
22
|
+
|
21
23
|
spec.add_development_dependency 'bundler', '>= 1.15'
|
22
24
|
end
|
@@ -1,18 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
module
|
3
|
+
module ActiveRecord
|
4
|
+
module KSUID
|
5
5
|
# A binary-serialized KSUID for storage within an ActiveRecord database
|
6
6
|
#
|
7
7
|
# @api private
|
8
8
|
#
|
9
9
|
# @example Set an attribute as a KSUID using the verbose syntax
|
10
|
-
# class
|
11
|
-
# attribute :ksuid, KSUID::
|
10
|
+
# class EventWithBareBinaryType < ActiveRecord::Base
|
11
|
+
# attribute :ksuid, ActiveRecord::KSUID::BinaryType.new, default: -> { KSUID.new }
|
12
12
|
# end
|
13
13
|
#
|
14
14
|
# @example Set an attribute as a KSUID using the pre-registered type
|
15
|
-
# class
|
15
|
+
# class EventWithRegisteredBinaryType < ActiveRecord::Base
|
16
16
|
# attribute :ksuid, :ksuid_binary, default: -> { KSUID.new }
|
17
17
|
# end
|
18
18
|
class BinaryType < ::ActiveRecord::Type::Binary
|
@@ -28,7 +28,7 @@ module KSUID
|
|
28
28
|
# @param value [String, Array<Integer>, KSUID::Type] the value to cast into a KSUID
|
29
29
|
# @return [KSUID::Type] the type-casted value
|
30
30
|
def cast(value)
|
31
|
-
KSUID.call(value)
|
31
|
+
::KSUID.call(value)
|
32
32
|
end
|
33
33
|
|
34
34
|
# Converts a value from database input to a KSUID
|
@@ -39,7 +39,7 @@ module KSUID
|
|
39
39
|
return unless value
|
40
40
|
|
41
41
|
value = value.to_s if value.is_a?(::ActiveRecord::Type::Binary::Data)
|
42
|
-
KSUID.call(value)
|
42
|
+
::KSUID.call(value)
|
43
43
|
end
|
44
44
|
|
45
45
|
# Casts the value from a KSUID into a database-understandable format
|
@@ -49,10 +49,10 @@ module KSUID
|
|
49
49
|
def serialize(value)
|
50
50
|
return unless value
|
51
51
|
|
52
|
-
super(KSUID.call(value).to_bytes)
|
52
|
+
super(::KSUID.call(value).to_bytes)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
ActiveRecord::Type.register(:ksuid_binary, KSUID::
|
58
|
+
ActiveRecord::Type.register(:ksuid_binary, ActiveRecord::KSUID::BinaryType)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module KSUID
|
5
|
+
# A string-serialized, prefixed KSUID for storage within an ActiveRecord database
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
# @since 0.5.0
|
9
|
+
#
|
10
|
+
# @example Set an attribute as a prefixed KSUID using the verbose syntax
|
11
|
+
# class EventWithBarePrefixedType < ActiveRecord::Base
|
12
|
+
# attribute(
|
13
|
+
# :ksuid,
|
14
|
+
# ActiveRecord::KSUID::PrefixedType.new(prefix: 'evt_'),
|
15
|
+
# default: -> { KSUID.prefixed('evt_') }
|
16
|
+
# )
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @example Set an attribute as a prefixed KSUID using the pre-registered type
|
20
|
+
# class EventWithRegisteredPrefixedType < ActiveRecord::Base
|
21
|
+
# attribute :ksuid, :ksuid_prefixed, prefix: 'evt_', default: -> { KSUID.prefixed('evt_') }
|
22
|
+
# end
|
23
|
+
class PrefixedType < ::ActiveRecord::Type::String
|
24
|
+
# Instantiates an ActiveRecord::Type for handling prefixed KSUIDs
|
25
|
+
#
|
26
|
+
# @param prefix [String] the prefix to add to the KSUID
|
27
|
+
def initialize(prefix: '')
|
28
|
+
@prefix = prefix
|
29
|
+
super()
|
30
|
+
end
|
31
|
+
|
32
|
+
# Casts a value from user input into a {KSUID::Prefixed}
|
33
|
+
#
|
34
|
+
# Type casting happens via the attribute setter and can take input from
|
35
|
+
# many places, including:
|
36
|
+
#
|
37
|
+
# 1. The Rails form builder
|
38
|
+
# 2. Directly from the attribute setter
|
39
|
+
# 3. From the model initializer
|
40
|
+
#
|
41
|
+
# @param value [String, Array<Integer>, KSUID::Prefixed] the value to cast into a KSUID
|
42
|
+
# @return [KSUID::Prefixed] the type-casted value
|
43
|
+
def cast(value)
|
44
|
+
::KSUID::Prefixed.call(value, prefix: @prefix)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Converts a value from database input to a {KSUID::Prefixed}
|
48
|
+
#
|
49
|
+
# @param value [String, nil] the database-serialized, prefixed KSUID to convert
|
50
|
+
# @return [KSUID::Prefixed] the deserialized, prefixed KSUID
|
51
|
+
def deserialize(value)
|
52
|
+
return unless value
|
53
|
+
|
54
|
+
::KSUID::Prefixed.from_base62(value, prefix: @prefix)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Casts the value from a KSUID into a database-understandable format
|
58
|
+
#
|
59
|
+
# @param value [KSUID::Prefixed, nil] the prefixed KSUID in Ruby format
|
60
|
+
# @return [String, nil] the base 62-encoded, prefixed KSUID for storage in the database
|
61
|
+
def serialize(value)
|
62
|
+
return unless value
|
63
|
+
|
64
|
+
::KSUID::Prefixed.call(value, prefix: @prefix).to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
ActiveRecord::Type.register(:ksuid_prefixed, ActiveRecord::KSUID::PrefixedType)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module KSUID
|
5
|
+
# Enables the usage of KSUID types within ActiveRecord when Rails is loaded
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class Railtie < ::Rails::Railtie
|
9
|
+
initializer 'ksuid' do
|
10
|
+
ActiveSupport.on_load :active_record do
|
11
|
+
require 'active_record/ksuid'
|
12
|
+
require 'ksuid/activerecord'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer 'ksuid.table_definition' do
|
17
|
+
ActiveSupport.on_load :active_record do
|
18
|
+
require 'active_record/ksuid/table_definition'
|
19
|
+
|
20
|
+
ActiveRecord::ConnectionAdapters::TableDefinition.include(
|
21
|
+
ActiveRecord::KSUID::TableDefinition
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
module
|
3
|
+
module ActiveRecord
|
4
|
+
module KSUID
|
5
5
|
# Extends ActiveRecord's table definition language for KSUIDs
|
6
6
|
module TableDefinition
|
7
7
|
# Defines a field as a string-based KSUID
|
@@ -22,9 +22,12 @@ module KSUID
|
|
22
22
|
#
|
23
23
|
# @param args [Array<Symbol>] the list of fields to define as KSUIDs
|
24
24
|
# @param options [Hash] see {ActiveRecord::ConnectionAdapters::TableDefinition}
|
25
|
+
# @option options [String] :prefix the prefix expected in front of the KSUID
|
25
26
|
# @return [void]
|
26
27
|
def ksuid(*args, **options)
|
27
|
-
|
28
|
+
prefix_length = options.delete(:prefix)&.length || 0
|
29
|
+
|
30
|
+
args.each { |name| column(name, :string, **options.merge(limit: 27 + prefix_length)) }
|
28
31
|
end
|
29
32
|
|
30
33
|
# Defines a field as a binary-based KSUID
|
@@ -52,5 +55,3 @@ module KSUID
|
|
52
55
|
end
|
53
56
|
end
|
54
57
|
end
|
55
|
-
|
56
|
-
ActiveRecord::ConnectionAdapters::TableDefinition.include(KSUID::ActiveRecord::TableDefinition)
|
@@ -1,18 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
module
|
3
|
+
module ActiveRecord
|
4
|
+
module KSUID
|
5
5
|
# A string-serialized KSUID for storage within an ActiveRecord database
|
6
6
|
#
|
7
7
|
# @api private
|
8
8
|
#
|
9
9
|
# @example Set an attribute as a KSUID using the verbose syntax
|
10
|
-
# class
|
11
|
-
# attribute :ksuid, KSUID::
|
10
|
+
# class EventWithBareType < ActiveRecord::Base
|
11
|
+
# attribute :ksuid, ActiveRecord::KSUID::Type.new, default: -> { KSUID.new }
|
12
12
|
# end
|
13
13
|
#
|
14
14
|
# @example Set an attribute as a KSUID using the pre-registered type
|
15
|
-
# class
|
15
|
+
# class EventWithRegisteredType < ActiveRecord::Base
|
16
16
|
# attribute :ksuid, :ksuid, default: -> { KSUID.new }
|
17
17
|
# end
|
18
18
|
class Type < ::ActiveRecord::Type::String
|
@@ -28,7 +28,7 @@ module KSUID
|
|
28
28
|
# @param value [String, Array<Integer>, KSUID::Type] the value to cast into a KSUID
|
29
29
|
# @return [KSUID::Type] the type-casted value
|
30
30
|
def cast(value)
|
31
|
-
KSUID.call(value)
|
31
|
+
::KSUID.call(value)
|
32
32
|
end
|
33
33
|
|
34
34
|
# Converts a value from database input to a KSUID
|
@@ -38,7 +38,7 @@ module KSUID
|
|
38
38
|
def deserialize(value)
|
39
39
|
return unless value
|
40
40
|
|
41
|
-
KSUID.from_base62(value)
|
41
|
+
::KSUID.from_base62(value)
|
42
42
|
end
|
43
43
|
|
44
44
|
# Casts the value from a KSUID into a database-understandable format
|
@@ -48,10 +48,10 @@ module KSUID
|
|
48
48
|
def serialize(value)
|
49
49
|
return unless value
|
50
50
|
|
51
|
-
KSUID.call(value).to_s
|
51
|
+
::KSUID.call(value).to_s
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
ActiveRecord::Type.register(:ksuid, KSUID::
|
57
|
+
ActiveRecord::Type.register(:ksuid, ActiveRecord::KSUID::Type)
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/ksuid/binary_type'
|
4
|
+
require 'active_record/ksuid/prefixed_type'
|
5
|
+
require 'active_record/ksuid/type'
|
6
|
+
|
7
|
+
# The Ruby on Rails object-relational mapper
|
8
|
+
#
|
9
|
+
# @see https://guides.rubyonrails.org/ Ruby on Rails documentation
|
10
|
+
module ActiveRecord
|
11
|
+
# Enables an Active Record model to have a KSUID attribute
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
# @since 0.5.0
|
15
|
+
module KSUID
|
16
|
+
# Builds a module to include into the model
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
#
|
20
|
+
# @example Add a `#ksuid` attribute to a model
|
21
|
+
# class Event < ActiveRecord::Base
|
22
|
+
# include ActiveRecord::KSUID[:ksuid]
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @example Add a `#remote_id` attribute to a model and overrides `#created_at` to use the KSUID
|
26
|
+
# class Event < ActiveRecord::Base
|
27
|
+
# include ActiveRecord::KSUID[:remote_id, created_at: true]
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# @example Add a prefixed `#ksuid` attribute to a model
|
31
|
+
# class Event < ActiveRecord::Base
|
32
|
+
# include ActiveRecord::KSUID[:ksuid, prefix: 'evt_']
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# @param field [String, Symbol] the name of the field to use as a KSUID
|
36
|
+
# @param created_at [Boolean] whether to override the `#created_at` method
|
37
|
+
# @param binary [Boolean] whether to store the KSUID as a binary or a string
|
38
|
+
# @param prefix [String, nil] a prefix to prepend to the KSUID attribute
|
39
|
+
# @return [Module] the module to include into the model
|
40
|
+
def self.[](field, created_at: false, binary: false, prefix: nil)
|
41
|
+
raise ArgumentError, 'cannot include a prefix on a binary KSUID' if binary && prefix
|
42
|
+
|
43
|
+
Module.new.tap do |mod|
|
44
|
+
if prefix
|
45
|
+
define_prefixed_attribute(field, mod, prefix)
|
46
|
+
else
|
47
|
+
define_attribute(field, mod, binary)
|
48
|
+
end
|
49
|
+
define_created_at(field, mod) if created_at
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Defines the attribute method that will be written in the module
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
#
|
57
|
+
# @param field [String, Symbol] the name of the field to set as an attribute
|
58
|
+
# @param mod [Module] the module to extend
|
59
|
+
# @param binary [Boolean] whether to store the KSUID as a binary or a string
|
60
|
+
# @return [void]
|
61
|
+
def self.define_attribute(field, mod, binary)
|
62
|
+
type = 'ksuid'
|
63
|
+
type = 'ksuid_binary' if binary
|
64
|
+
|
65
|
+
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
66
|
+
def self.included(base) # def self.included(base)
|
67
|
+
base.__send__( # base.__send__(
|
68
|
+
:attribute, # :attribute,
|
69
|
+
:#{field}, # :id,
|
70
|
+
:#{type}, # :ksuid,
|
71
|
+
default: -> { ::KSUID.new } # default: -> { ::KSUID.new }
|
72
|
+
) # )
|
73
|
+
end # end
|
74
|
+
RUBY
|
75
|
+
end
|
76
|
+
private_class_method :define_attribute
|
77
|
+
|
78
|
+
# Defines the attribute method that will be written in the module for a field
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
#
|
82
|
+
# @param field [String, Symbol] the name of the field to set as an attribute
|
83
|
+
# @param mod [Module] the module to extend
|
84
|
+
# @param prefix [String] the prefix to add to the KSUID
|
85
|
+
# @return [void]
|
86
|
+
def self.define_prefixed_attribute(field, mod, prefix)
|
87
|
+
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
88
|
+
def self.included(base) # def self.included(base)
|
89
|
+
base.__send__( # base.__send__(
|
90
|
+
:attribute, # :attribute,
|
91
|
+
:#{field}, # :id,
|
92
|
+
:ksuid_prefixed, # :ksuid_prefixed,
|
93
|
+
prefix: #{prefix.inspect}, # prefix: 'evt_'
|
94
|
+
default: -> { ::KSUID.prefixed(#{prefix.inspect}) } # default: -> { ::KSUID.prefixed('evt_') }
|
95
|
+
) # )
|
96
|
+
end # end
|
97
|
+
RUBY
|
98
|
+
end
|
99
|
+
private_class_method :define_prefixed_attribute
|
100
|
+
|
101
|
+
# Defines the `#created_at` method that will be written in the module
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
#
|
105
|
+
# @param field [String, Symbol] the name of the KSUID attribute field
|
106
|
+
# @param mod [Module] the module to extend
|
107
|
+
# @return [void]
|
108
|
+
def self.define_created_at(field, mod)
|
109
|
+
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
110
|
+
def created_at # def created_at
|
111
|
+
return unless #{field} # return unless ksuid
|
112
|
+
|
113
|
+
#{field}.to_time # ksuid.to_time
|
114
|
+
end # end
|
115
|
+
RUBY
|
116
|
+
end
|
117
|
+
private_class_method :define_created_at
|
118
|
+
end
|
119
|
+
end
|
data/lib/ksuid/activerecord.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'ksuid
|
4
|
-
require 'ksuid/activerecord/type'
|
3
|
+
require 'active_record/ksuid'
|
5
4
|
|
6
5
|
module KSUID
|
7
6
|
# Enables an Active Record model to have a KSUID attribute
|
8
7
|
#
|
9
8
|
# @api public
|
9
|
+
# @deprecated Use {ActiveRecord::KSUID} instead.
|
10
10
|
module ActiveRecord
|
11
11
|
# Builds a module to include into the model
|
12
12
|
#
|
13
13
|
# @api public
|
14
|
+
# @deprecated Use {::ActiveRecord::KSUID.[]} instead.
|
14
15
|
#
|
15
16
|
# @example Add a `#ksuid` attribute to a model
|
16
17
|
# class Event < ActiveRecord::Base
|
@@ -22,54 +23,15 @@ module KSUID
|
|
22
23
|
# include KSUID::ActiveRecord[:remote_id, created_at: true]
|
23
24
|
# end
|
24
25
|
#
|
25
|
-
# @param
|
26
|
-
# @
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
.tap do |mod|
|
33
|
-
define_attribute(field, mod, binary)
|
34
|
-
define_created_at(field, mod) if created_at
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Defines the attribute method that will be written in the module
|
39
|
-
#
|
40
|
-
# @api private
|
41
|
-
#
|
42
|
-
# @param field [String, Symbol] the name of the field to set as an attribute
|
43
|
-
# @param mod [Module] the module to extend
|
44
|
-
# @return [void]
|
45
|
-
def self.define_attribute(field, mod, binary)
|
46
|
-
type = 'ksuid'
|
47
|
-
type = 'ksuid_binary' if binary
|
48
|
-
|
49
|
-
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
50
|
-
def self.included(base)
|
51
|
-
base.__send__(:attribute, :#{field}, :#{type}, default: -> { KSUID.new })
|
52
|
-
end
|
53
|
-
RUBY
|
54
|
-
end
|
55
|
-
private_class_method :define_attribute
|
56
|
-
|
57
|
-
# Defines the `#created_at` method that will be written in the module
|
58
|
-
#
|
59
|
-
# @api private
|
60
|
-
#
|
61
|
-
# @param field [String, Symbol] the name of the KSUID attribute field
|
62
|
-
# @param mod [Module] the module to extend
|
63
|
-
# @return [void]
|
64
|
-
def self.define_created_at(field, mod)
|
65
|
-
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
66
|
-
def created_at
|
67
|
-
return unless #{field}
|
26
|
+
# @param (see ::ActiveRecord::KSUID.[])
|
27
|
+
# @return (see ::ActiveRecord::KSUID.[])
|
28
|
+
def self.[](field, created_at: false, binary: false, prefix: nil)
|
29
|
+
ActiveSupport::Deprecation.instance.warn(
|
30
|
+
'KSUID::ActiveRecord is deprecated! Use ActiveRecord::KSUID instead.',
|
31
|
+
caller_locations
|
32
|
+
)
|
68
33
|
|
69
|
-
|
70
|
-
end
|
71
|
-
RUBY
|
34
|
+
::ActiveRecord::KSUID[field, created_at: created_at, binary: binary, prefix: prefix]
|
72
35
|
end
|
73
|
-
private_class_method :define_created_at
|
74
36
|
end
|
75
37
|
end
|
data/lib/ksuid/base62.rb
CHANGED
@@ -35,7 +35,7 @@ module KSUID
|
|
35
35
|
# @param string [String] the string to check for compatibility
|
36
36
|
# @return [Boolean]
|
37
37
|
def self.compatible?(string)
|
38
|
-
|
38
|
+
!MATCHER.match?(string)
|
39
39
|
end
|
40
40
|
|
41
41
|
# Decodes a base 62-encoded string into an integer
|
@@ -51,12 +51,12 @@ module KSUID
|
|
51
51
|
def self.decode(ksuid)
|
52
52
|
result = 0
|
53
53
|
|
54
|
-
ksuid.
|
54
|
+
ksuid.chars.each_with_index do |char, position|
|
55
55
|
unless (digit = CHARSET.index(char))
|
56
56
|
raise(ArgumentError, "#{ksuid} is not a base 62 number")
|
57
57
|
end
|
58
58
|
|
59
|
-
result += digit * BASE**(ksuid.length - (position + 1))
|
59
|
+
result += digit * (BASE**(ksuid.length - (position + 1)))
|
60
60
|
end
|
61
61
|
|
62
62
|
result
|
@@ -76,7 +76,7 @@ module KSUID
|
|
76
76
|
chars = encode_without_padding(number)
|
77
77
|
|
78
78
|
chars << padding if chars.empty?
|
79
|
-
chars.reverse.join
|
79
|
+
chars.reverse.join.rjust(STRING_LENGTH, padding)
|
80
80
|
end
|
81
81
|
|
82
82
|
# Encodes a byte string or byte array into base 62
|
data/lib/ksuid/configuration.rb
CHANGED
@@ -87,8 +87,11 @@ module KSUID
|
|
87
87
|
def assert_payload_size(generator)
|
88
88
|
return if (length = generator.call.length) == (expected_length = BYTES[:payload])
|
89
89
|
|
90
|
-
raise
|
90
|
+
raise(
|
91
|
+
ConfigurationError,
|
92
|
+
'Random generator generates the wrong number of bytes ' \
|
91
93
|
"(#{length} generated, #{expected_length} expected)"
|
94
|
+
)
|
92
95
|
end
|
93
96
|
end
|
94
97
|
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KSUID
|
4
|
+
# Encapsulates the data type for a prefixed KSUID
|
5
|
+
#
|
6
|
+
# When you have different types of KSUIDs in your application, it can be
|
7
|
+
# helpful to add an identifier to the front of them to give you an idea for
|
8
|
+
# what kind of object the KSUID belongs to.
|
9
|
+
#
|
10
|
+
# For example, you might use KSUIDs to identify both Events and Customers. For
|
11
|
+
# an Event, you could prefix the KSUID with the string `evt_`. Likewise, for
|
12
|
+
# Customers, you could prefix them with the string `cus_`.
|
13
|
+
#
|
14
|
+
# {KSUID::Prefixed} gives you affordances for doing just this.
|
15
|
+
#
|
16
|
+
# ## Ordering
|
17
|
+
#
|
18
|
+
# {KSUID::Prefixed}s are partially orderable with {KSUID::Type} by their
|
19
|
+
# timestamps. When ordering them with other {KSUID::Prefixed} instances, they
|
20
|
+
# order first by prefix, then by timestamp. This means that in a mixed
|
21
|
+
# collection, all Customer KSUIDs (prefix: `cus_`) would be grouped before all
|
22
|
+
# Event KSUIDs (prefix `evt_`).
|
23
|
+
#
|
24
|
+
# ## Interface
|
25
|
+
#
|
26
|
+
# You typically will not instantiate this class directly, but instead use the
|
27
|
+
# {KSUID.prefixed} builder method to save some typing.
|
28
|
+
#
|
29
|
+
# The most commonly used helper methods for the {KSUID} module also exist on
|
30
|
+
# {KSUID::Prefixed} for converting between different forms of output.
|
31
|
+
#
|
32
|
+
# ## Differences from {KSUID::Type}
|
33
|
+
#
|
34
|
+
# One other thing to note is that {KSUID::Prefixed} is not intended to handle
|
35
|
+
# binary data because the prefix does not make sense in either the byte string
|
36
|
+
# or packed array formats.
|
37
|
+
#
|
38
|
+
# @since 0.5.0
|
39
|
+
class Prefixed < Type
|
40
|
+
include Comparable
|
41
|
+
|
42
|
+
# Converts a KSUID-compatible value into a {KSUID::Prefixed}
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
#
|
46
|
+
# @example Converts a base 62 KSUID string into a {KSUID::Prefixed}
|
47
|
+
# KSUID::Prefixed.call('15Ew2nYeRDscBipuJicYjl970D1', prefix: 'evt_')
|
48
|
+
#
|
49
|
+
# @param ksuid [String, KSUID::Prefixed, KSUID::Type] the prefixed KSUID-compatible value
|
50
|
+
# @return [KSUID::Prefixed] the converted, prefixed KSUID
|
51
|
+
# @raise [ArgumentError] if the value is not prefixed KSUID-compatible
|
52
|
+
def self.call(ksuid, prefix:)
|
53
|
+
return unless ksuid && prefix
|
54
|
+
|
55
|
+
case ksuid
|
56
|
+
when KSUID::Prefixed then from_base62(ksuid.to_ksuid.to_s, prefix: prefix)
|
57
|
+
when KSUID::Type then from_base62(ksuid.to_s, prefix: prefix)
|
58
|
+
when String then cast_string(ksuid, prefix: prefix)
|
59
|
+
else
|
60
|
+
raise ArgumentError, "Cannot convert #{ksuid.inspect} to KSUID::Prefixed"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Converts a base 62-encoded string into a {KSUID::Prefixed}
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
#
|
68
|
+
# @example Parse a KSUID string into a prefixed object
|
69
|
+
# KSUID::Prefixed.from_base62('0vdbMgWkU6slGpLVCqEFwkkZvuW', prefix: 'evt_')
|
70
|
+
#
|
71
|
+
# @param string [String] the base 62-encoded KSUID to convert into an object
|
72
|
+
# @param prefix [String] the prefix to add to the KSUID
|
73
|
+
# @return [KSUID::Prefixed] the prefixed KSUID generated from the string
|
74
|
+
def self.from_base62(string, prefix:)
|
75
|
+
string = string.sub(/\A#{prefix}/, '')
|
76
|
+
int = Base62.decode(string)
|
77
|
+
bytes = Utils.int_to_bytes(int, 160)
|
78
|
+
|
79
|
+
from_bytes(bytes, prefix: prefix)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Casts a string into a {KSUID::Prefixed}
|
83
|
+
#
|
84
|
+
# @api private
|
85
|
+
#
|
86
|
+
# @param ksuid [String] the string to convert into a {KSUID::Prefixed}
|
87
|
+
# @param prefix [String] the prefix to prepend to the KSUID
|
88
|
+
# @return [KSUID::Prefixed] the converted, prefixed KSUID
|
89
|
+
def self.cast_string(ksuid, prefix:)
|
90
|
+
ksuid = ksuid[-KSUID::BYTES[:base62]..-1] if ksuid.length >= KSUID::BYTES[:base62]
|
91
|
+
|
92
|
+
unless Base62.compatible?(ksuid)
|
93
|
+
raise ArgumentError, 'Prefixed KSUIDs cannot be binary strings'
|
94
|
+
end
|
95
|
+
|
96
|
+
from_base62(ksuid, prefix: prefix)
|
97
|
+
end
|
98
|
+
private_class_method :cast_string
|
99
|
+
|
100
|
+
# Converts a byte string or byte array into a KSUID
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
#
|
104
|
+
# @param bytes [String] the byte string to convert into an object
|
105
|
+
# @return [KSUID::Prefixed] the prefixed KSUID generated from the bytes
|
106
|
+
def self.from_bytes(bytes, prefix:)
|
107
|
+
bytes = bytes.bytes
|
108
|
+
timestamp = Utils.int_from_bytes(bytes.first(KSUID::BYTES[:timestamp]))
|
109
|
+
payload = Utils.byte_string_from_array(bytes.last(KSUID::BYTES[:payload]))
|
110
|
+
|
111
|
+
new(prefix, payload: payload, time: Time.at(timestamp + EPOCH_TIME))
|
112
|
+
end
|
113
|
+
private_class_method :from_bytes
|
114
|
+
|
115
|
+
# Instantiates a new {KSUID::Prefixed}
|
116
|
+
#
|
117
|
+
# @api semipublic
|
118
|
+
#
|
119
|
+
# @example Generate a new {KSUID::Prefixed} for the current second
|
120
|
+
# KSUID::Prefixed.new('evt_')
|
121
|
+
#
|
122
|
+
# @example Generate a new {KSUID::Prefixed} for a given timestamp
|
123
|
+
# KSUID::Prefixed.new('cus_', time: Time.parse('2017-11-05 15:00:04 UTC'))
|
124
|
+
#
|
125
|
+
# @param prefix [String] the prefix to add to the KSUID
|
126
|
+
# @param payload [String, Array<Integer>, nil] the payload for the KSUID
|
127
|
+
# @param time [Time] the timestamp to use for the KSUID
|
128
|
+
# @return [KSUID::Prefix] the generated, prefixed KSUID
|
129
|
+
def initialize(prefix, payload: nil, time: Time.now)
|
130
|
+
raise ArgumentError, 'requires a prefix' unless prefix
|
131
|
+
|
132
|
+
super(payload: payload, time: time)
|
133
|
+
|
134
|
+
@prefix = prefix
|
135
|
+
end
|
136
|
+
|
137
|
+
# The prefix in front of the KSUID
|
138
|
+
#
|
139
|
+
# @api semipublic
|
140
|
+
#
|
141
|
+
# @example Getting the prefix to create a similar {KSUID::Prefixed}
|
142
|
+
# ksuid1 = KSUID.prefixed('cus_')
|
143
|
+
# ksuid2 = KSUID.prefixed(ksuid1.prefix)
|
144
|
+
#
|
145
|
+
# @return [String] the prefix of the {KSUID::Prefixed}
|
146
|
+
attr_reader :prefix
|
147
|
+
|
148
|
+
# Implements the Comparable interface for sorting {KSUID::Prefixed}s
|
149
|
+
#
|
150
|
+
# @api private
|
151
|
+
#
|
152
|
+
# @param other [KSUID::Type] the other object to compare against
|
153
|
+
# @return [Integer, nil] nil for uncomparable, -1 for less than other,
|
154
|
+
# 0 for equal to, 1 for greater than other
|
155
|
+
def <=>(other)
|
156
|
+
return unless other.is_a?(Type)
|
157
|
+
return super if other.instance_of?(Type)
|
158
|
+
|
159
|
+
if (result = prefix <=> other.prefix).nonzero?
|
160
|
+
result
|
161
|
+
else
|
162
|
+
super
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Checks whether this {KSUID::Prefixed} is equal to another
|
167
|
+
#
|
168
|
+
# @api semipublic
|
169
|
+
#
|
170
|
+
# @example Checks whether two KSUIDs are equal
|
171
|
+
# KSUID.prefixed('evt_') == KSUID.prefixed('evt_')
|
172
|
+
#
|
173
|
+
# @param other [KSUID::Prefixed] the other {KSUID::Prefixed} to check against
|
174
|
+
# @return [Boolean]
|
175
|
+
def ==(other)
|
176
|
+
other.is_a?(Prefixed) &&
|
177
|
+
prefix == other.prefix &&
|
178
|
+
super
|
179
|
+
end
|
180
|
+
|
181
|
+
# Generates the key to use when using a {KSUID::Prefixed} as a hash key
|
182
|
+
#
|
183
|
+
# @api semipublic
|
184
|
+
#
|
185
|
+
# @example Using a KSUID as a Hash key
|
186
|
+
# ksuid1 = KSUID.prefixed('evt_')
|
187
|
+
# ksuid2 = ksuid1.dup
|
188
|
+
# values_by_ksuid = {}
|
189
|
+
#
|
190
|
+
# values_by_ksuid[ksuid1] = "example"
|
191
|
+
# values_by_ksuid[ksuid2] #=> "example"
|
192
|
+
#
|
193
|
+
# @return [Integer]
|
194
|
+
def hash
|
195
|
+
[prefix, @uid].hash
|
196
|
+
end
|
197
|
+
|
198
|
+
# The {KSUID::Prefixed} as a prefixed, hex-encoded string
|
199
|
+
#
|
200
|
+
# This is generally useful for comparing against the Go tool.
|
201
|
+
#
|
202
|
+
# @api public
|
203
|
+
#
|
204
|
+
# @example
|
205
|
+
# ksuid = KSUID::Prefixed.from_base62('0vdbMgWkU6slGpLVCqEFwkkZvuW', prefix: 'evt_')
|
206
|
+
#
|
207
|
+
# ksuid.raw #=> "evt_0683F789049CC215C099D42B784DBE99341BD79C"
|
208
|
+
#
|
209
|
+
# @return [String] a prefixed, hex-encoded string
|
210
|
+
def raw
|
211
|
+
super.prepend(prefix)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Converts the {KSUID::Prefixed} into a {KSUID::Type} by dropping the prefix
|
215
|
+
#
|
216
|
+
# @api public
|
217
|
+
#
|
218
|
+
# @example Convert an Event KSUID into a plain KSUID
|
219
|
+
# ksuid = KSUID.prefixed('evt_')
|
220
|
+
#
|
221
|
+
# ksuid.to_ksuid
|
222
|
+
#
|
223
|
+
# @return [KSUID::Type] the non-prefixed KSUID
|
224
|
+
def to_ksuid
|
225
|
+
KSUID.from_base62(to_s.sub(/\A#{prefix}/, ''))
|
226
|
+
end
|
227
|
+
|
228
|
+
# The {KSUID::Prefixed} as a base 62-encoded string
|
229
|
+
#
|
230
|
+
# @api public
|
231
|
+
#
|
232
|
+
# @example
|
233
|
+
# ksuid = KSUID::Prefixed.from_base62('0vdbMgWkU6slGpLVCqEFwkkZvuW', prefix: 'evt_')
|
234
|
+
#
|
235
|
+
# ksuid.to_s #=> "evt_0vdbMgWkU6slGpLVCqEFwkkZvuW"
|
236
|
+
#
|
237
|
+
# @return [String] the prefixed, base 62-encoded string for the {KSUID::Prefixed}
|
238
|
+
def to_s
|
239
|
+
super.prepend(prefix)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
data/lib/ksuid/type.rb
CHANGED
data/lib/ksuid/utils.rb
CHANGED
data/lib/ksuid/version.rb
CHANGED
data/lib/ksuid.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'ksuid/configuration'
|
4
|
-
require_relative 'ksuid/type'
|
5
4
|
require_relative 'ksuid/version'
|
6
5
|
|
7
6
|
# The K-Sortable Unique IDentifier (KSUID)
|
@@ -41,6 +40,9 @@ require_relative 'ksuid/version'
|
|
41
40
|
# @example Generate a new KSUID
|
42
41
|
# KSUID.new
|
43
42
|
#
|
43
|
+
# @example Generate a KSUID prefixed by `evt_`
|
44
|
+
# KSUID.prefixed('evt_')
|
45
|
+
#
|
44
46
|
# @example Parse a KSUID string that you have received
|
45
47
|
# KSUID.from_base62('aWgEPTl1tmebfsQzFP4bxwgy80V')
|
46
48
|
#
|
@@ -63,7 +65,7 @@ module KSUID
|
|
63
65
|
# The number of bytes that are used to represent each part of a KSUID
|
64
66
|
#
|
65
67
|
# @return [Hash{Symbol => Integer}] the map of data type to number of bytes
|
66
|
-
BYTES = { payload: 16, timestamp: 4, total: 20 }.freeze
|
68
|
+
BYTES = { base62: 27, payload: 16, timestamp: 4, total: 20 }.freeze
|
67
69
|
|
68
70
|
# The number of characters in a base 62-encoded KSUID
|
69
71
|
#
|
@@ -75,6 +77,11 @@ module KSUID
|
|
75
77
|
# @return [String]
|
76
78
|
MAX_STRING_ENCODED = 'aWgEPTl1tmebfsQzFP4bxwgy80V'
|
77
79
|
|
80
|
+
autoload :Base62, 'ksuid/base62'
|
81
|
+
autoload :Prefixed, 'ksuid/prefixed'
|
82
|
+
autoload :Type, 'ksuid/type'
|
83
|
+
autoload :Utils, 'ksuid/utils'
|
84
|
+
|
78
85
|
# Converts a KSUID-compatible value into an actual KSUID
|
79
86
|
#
|
80
87
|
# @api public
|
@@ -89,6 +96,7 @@ module KSUID
|
|
89
96
|
return unless ksuid
|
90
97
|
|
91
98
|
case ksuid
|
99
|
+
when KSUID::Prefixed then ksuid.to_ksuid
|
92
100
|
when KSUID::Type then ksuid
|
93
101
|
when Array then KSUID.from_bytes(ksuid)
|
94
102
|
when String then cast_string(ksuid)
|
@@ -190,6 +198,43 @@ module KSUID
|
|
190
198
|
Type.new(payload: payload, time: time)
|
191
199
|
end
|
192
200
|
|
201
|
+
# Instantiates a new {KSUID::Prefixed}
|
202
|
+
#
|
203
|
+
# @api public
|
204
|
+
# @since 0.5.0
|
205
|
+
#
|
206
|
+
# @example Generate a new prefixed KSUID for the current second
|
207
|
+
# KSUID.prefixed('evt_')
|
208
|
+
#
|
209
|
+
# @example Generate a new prefixed KSUID for a given timestamp
|
210
|
+
# KSUID.prefixed('cus_', time: Time.parse('2022-08-16 10:36:00 UTC'))
|
211
|
+
#
|
212
|
+
# @param prefix [String] the prefix to apply to the KSUID
|
213
|
+
# @param payload [String, Array<Integer>, nil] the payload for the KSUID
|
214
|
+
# @param time [Time] the timestamp to use for the KSUID
|
215
|
+
# @return [KSUID::Prefixed] the generated, prefixed KSUID
|
216
|
+
def self.prefixed(prefix, payload: nil, time: Time.now)
|
217
|
+
Prefixed.new(prefix, payload: payload, time: time)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Generates a KSUID string
|
221
|
+
#
|
222
|
+
# @api public
|
223
|
+
# @since 0.5.0
|
224
|
+
#
|
225
|
+
# @example Generate a new KSUID string for the current second
|
226
|
+
# KSUID.string
|
227
|
+
#
|
228
|
+
# @example Generate a new KSUID string for a given timestamp
|
229
|
+
# KSUID.string(time: Time.parse('2017-11-05 15:00:04 UTC'))
|
230
|
+
#
|
231
|
+
# @param payload [String, Array<Integer>, nil] the payload for the KSUID string
|
232
|
+
# @param time [Time] the timestamp to use for the KSUID string
|
233
|
+
# @return [String] the generated string
|
234
|
+
def self.string(payload: nil, time: Time.now)
|
235
|
+
Type.new(payload: payload, time: time).to_s
|
236
|
+
end
|
237
|
+
|
193
238
|
# Casts a string into a KSUID
|
194
239
|
#
|
195
240
|
# @api private
|
@@ -206,4 +251,4 @@ module KSUID
|
|
206
251
|
private_class_method :cast_string
|
207
252
|
end
|
208
253
|
|
209
|
-
require 'ksuid/railtie' if defined?(Rails)
|
254
|
+
require 'active_record/ksuid/railtie' if defined?(Rails)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ksuid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Herold
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -35,22 +35,27 @@ files:
|
|
35
35
|
- CONTRIBUTING.md
|
36
36
|
- LICENSE.md
|
37
37
|
- README.md
|
38
|
+
- UPGRADING.md
|
38
39
|
- ksuid.gemspec
|
40
|
+
- lib/active_record/ksuid.rb
|
41
|
+
- lib/active_record/ksuid/binary_type.rb
|
42
|
+
- lib/active_record/ksuid/prefixed_type.rb
|
43
|
+
- lib/active_record/ksuid/railtie.rb
|
44
|
+
- lib/active_record/ksuid/table_definition.rb
|
45
|
+
- lib/active_record/ksuid/type.rb
|
39
46
|
- lib/ksuid.rb
|
40
47
|
- lib/ksuid/activerecord.rb
|
41
|
-
- lib/ksuid/activerecord/binary_type.rb
|
42
|
-
- lib/ksuid/activerecord/table_definition.rb
|
43
|
-
- lib/ksuid/activerecord/type.rb
|
44
48
|
- lib/ksuid/base62.rb
|
45
49
|
- lib/ksuid/configuration.rb
|
46
|
-
- lib/ksuid/
|
50
|
+
- lib/ksuid/prefixed.rb
|
47
51
|
- lib/ksuid/type.rb
|
48
52
|
- lib/ksuid/utils.rb
|
49
53
|
- lib/ksuid/version.rb
|
50
54
|
homepage: https://github.com/michaelherold/ksuid-ruby
|
51
55
|
licenses:
|
52
56
|
- MIT
|
53
|
-
metadata:
|
57
|
+
metadata:
|
58
|
+
rubygems_mfa_required: 'true'
|
54
59
|
post_install_message:
|
55
60
|
rdoc_options: []
|
56
61
|
require_paths:
|
data/lib/ksuid/railtie.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module KSUID
|
4
|
-
# Enables the usage of KSUID types within ActiveRecord when Rails is loaded
|
5
|
-
#
|
6
|
-
# @api private
|
7
|
-
class Railtie < ::Rails::Railtie
|
8
|
-
initializer 'ksuid' do
|
9
|
-
ActiveSupport.on_load :active_record do
|
10
|
-
require 'ksuid/activerecord'
|
11
|
-
require 'ksuid/activerecord/table_definition'
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|