encoded_id 1.0.0.rc5 → 1.0.0.rc6
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/CHANGELOG.md +78 -2
- data/README.md +79 -333
- data/context/encoded_id.md +283 -0
- data/lib/encoded_id/alphabet.rb +32 -3
- data/lib/encoded_id/blocklist.rb +90 -0
- data/lib/encoded_id/encoders/base.rb +71 -0
- data/lib/encoded_id/encoders/hash_id.rb +531 -0
- data/lib/encoded_id/encoders/hash_id_consistent_shuffle.rb +110 -0
- data/lib/encoded_id/encoders/hash_id_ordinal_alphabet_separator_guards.rb +270 -0
- data/lib/encoded_id/encoders/hash_id_salt.rb +51 -0
- data/lib/encoded_id/encoders/my_sqids.rb +465 -0
- data/lib/encoded_id/encoders/sqids.rb +42 -0
- data/lib/encoded_id/hex_representation.rb +23 -5
- data/lib/encoded_id/reversible_id.rb +109 -23
- data/lib/encoded_id/version.rb +4 -1
- data/lib/encoded_id.rb +38 -5
- metadata +14 -23
- data/.devcontainer/Dockerfile +0 -9
- data/.devcontainer/compose.yml +0 -8
- data/.devcontainer/devcontainer.json +0 -8
- data/.standard.yml +0 -2
- data/Gemfile +0 -36
- data/Rakefile +0 -20
- data/Steepfile +0 -5
- data/ext/encoded_id/extconf.rb +0 -3
- data/ext/encoded_id/extension.c +0 -123
- data/ext/encoded_id/hashids.c +0 -939
- data/ext/encoded_id/hashids.h +0 -139
- data/lib/encoded_id/hash_id.rb +0 -227
- data/lib/encoded_id/hash_id_consistent_shuffle.rb +0 -27
- data/lib/encoded_id/hash_id_salt.rb +0 -15
- data/lib/encoded_id/ordinal_alphabet_separator_guards.rb +0 -90
- data/rbs_collection.yaml +0 -24
- data/sig/encoded_id.rbs +0 -189
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
3
5
|
# Hashid with a reduced character set Crockford alphabet and split groups
|
|
4
6
|
# See: https://www.crockford.com/wrmg/base32.html
|
|
5
|
-
# Build with https://hashids.org
|
|
6
|
-
# Note hashIds already has a built in profanity limitation algorithm
|
|
7
|
+
# Build with support for https://hashids.org and https://sqids.org
|
|
7
8
|
module EncodedId
|
|
9
|
+
# @rbs!
|
|
10
|
+
# type encodeableValue = Array[String | Integer] | String | Integer
|
|
11
|
+
|
|
8
12
|
class ReversibleId
|
|
9
|
-
|
|
13
|
+
# @rbs VALID_ENCODERS: Array[Symbol]
|
|
14
|
+
VALID_ENCODERS = [:hashids, :sqids].freeze
|
|
15
|
+
# @rbs DEFAULT_ENCODER: Symbol
|
|
16
|
+
DEFAULT_ENCODER = :hashids
|
|
17
|
+
|
|
18
|
+
# @rbs @alphabet: Alphabet
|
|
19
|
+
# @rbs @salt: String
|
|
20
|
+
# @rbs @length: Integer
|
|
21
|
+
# @rbs @split_at: Integer?
|
|
22
|
+
# @rbs @split_with: String?
|
|
23
|
+
# @rbs @hex_represention_encoder: HexRepresentation
|
|
24
|
+
# @rbs @max_length: Integer?
|
|
25
|
+
# @rbs @max_inputs_per_id: Integer
|
|
26
|
+
# @rbs @blocklist: Blocklist
|
|
27
|
+
# @rbs @encoder: Encoders::Base
|
|
28
|
+
|
|
29
|
+
# @rbs (salt: String, ?length: Integer, ?split_at: Integer?, ?split_with: String?, ?alphabet: Alphabet, ?hex_digit_encoding_group_size: Integer, ?max_length: Integer?, ?max_inputs_per_id: Integer, ?encoder: Symbol | Encoders::Base, ?blocklist: Blocklist | Array[String] | Set[String] | nil) -> void
|
|
30
|
+
def initialize(salt:, length: 8, split_at: 4, split_with: "-", alphabet: Alphabet.modified_crockford, hex_digit_encoding_group_size: 4, max_length: 128, max_inputs_per_id: 32, encoder: DEFAULT_ENCODER, blocklist: Blocklist.empty)
|
|
10
31
|
@alphabet = validate_alphabet(alphabet)
|
|
11
32
|
@salt = validate_salt(salt)
|
|
12
33
|
@length = validate_length(length)
|
|
@@ -15,13 +36,27 @@ module EncodedId
|
|
|
15
36
|
@hex_represention_encoder = HexRepresentation.new(hex_digit_encoding_group_size)
|
|
16
37
|
@max_length = validate_max_length(max_length)
|
|
17
38
|
@max_inputs_per_id = validate_max_input(max_inputs_per_id)
|
|
39
|
+
@blocklist = validate_blocklist(blocklist)
|
|
40
|
+
@encoder = create_encoder(validate_encoder(encoder))
|
|
18
41
|
end
|
|
19
42
|
|
|
43
|
+
# Accessors for introspection
|
|
44
|
+
attr_reader :salt #: String
|
|
45
|
+
attr_reader :length #: Integer
|
|
46
|
+
attr_reader :alphabet #: Alphabet
|
|
47
|
+
attr_reader :split_at #: Integer?
|
|
48
|
+
attr_reader :split_with #: String?
|
|
49
|
+
attr_reader :hex_represention_encoder #: HexRepresentation
|
|
50
|
+
attr_reader :max_length #: Integer?
|
|
51
|
+
attr_reader :blocklist #: Blocklist
|
|
52
|
+
attr_reader :encoder #: Encoders::Base
|
|
53
|
+
|
|
20
54
|
# Encode the input values into a hash
|
|
55
|
+
# @rbs (encodeableValue values) -> String
|
|
21
56
|
def encode(values)
|
|
22
57
|
inputs = prepare_input(values)
|
|
23
|
-
encoded_id =
|
|
24
|
-
encoded_id = humanize_length(encoded_id)
|
|
58
|
+
encoded_id = encoder.encode(inputs)
|
|
59
|
+
encoded_id = humanize_length(encoded_id) if split_with && split_at
|
|
25
60
|
|
|
26
61
|
raise EncodedIdLengthError if max_length_exceeded?(encoded_id)
|
|
27
62
|
|
|
@@ -29,78 +64,83 @@ module EncodedId
|
|
|
29
64
|
end
|
|
30
65
|
|
|
31
66
|
# Encode hex strings into a hash
|
|
67
|
+
# @rbs (encodeableHexValue hexs) -> String
|
|
32
68
|
def encode_hex(hexs)
|
|
33
69
|
encode(hex_represention_encoder.hex_as_integers(hexs))
|
|
34
70
|
end
|
|
35
71
|
|
|
36
72
|
# Decode the hash to original array
|
|
73
|
+
# @rbs (String str, ?downcase: bool) -> Array[Integer]
|
|
37
74
|
def decode(str, downcase: true)
|
|
38
75
|
raise EncodedIdFormatError, "Max length of input exceeded" if max_length_exceeded?(str)
|
|
39
76
|
|
|
40
|
-
|
|
77
|
+
encoder.decode(convert_to_hash(str, downcase))
|
|
41
78
|
rescue InvalidInputError => e
|
|
42
79
|
raise EncodedIdFormatError, e.message
|
|
43
80
|
end
|
|
44
81
|
|
|
45
82
|
# Decode hex strings from a hash
|
|
83
|
+
# @rbs (String str, ?downcase: bool) -> Array[String]
|
|
46
84
|
def decode_hex(str, downcase: true)
|
|
47
|
-
integers =
|
|
85
|
+
integers = encoder.decode(convert_to_hash(str, downcase))
|
|
48
86
|
hex_represention_encoder.integers_as_hex(integers)
|
|
49
87
|
end
|
|
50
88
|
|
|
51
89
|
private
|
|
52
90
|
|
|
53
|
-
|
|
54
|
-
:length,
|
|
55
|
-
:alphabet,
|
|
56
|
-
:split_at,
|
|
57
|
-
:split_with,
|
|
58
|
-
:hex_represention_encoder,
|
|
59
|
-
:max_length
|
|
60
|
-
|
|
91
|
+
# @rbs (Alphabet alphabet) -> Alphabet
|
|
61
92
|
def validate_alphabet(alphabet)
|
|
62
93
|
return alphabet if alphabet.is_a?(Alphabet)
|
|
63
94
|
raise InvalidAlphabetError, "alphabet must be an instance of Alphabet"
|
|
64
95
|
end
|
|
65
96
|
|
|
97
|
+
# @rbs (String salt) -> String
|
|
66
98
|
def validate_salt(salt)
|
|
67
99
|
return salt if salt.is_a?(String) && salt.size > 3
|
|
68
100
|
raise InvalidConfigurationError, "Salt must be a string and longer than 3 characters"
|
|
69
101
|
end
|
|
70
102
|
|
|
71
103
|
# Target length of the encoded string (the minimum but not maximum length)
|
|
104
|
+
# @rbs (Integer length) -> Integer
|
|
72
105
|
def validate_length(length)
|
|
73
106
|
return length if valid_integer_option?(length)
|
|
74
107
|
raise InvalidConfigurationError, "Length must be an integer greater than 0"
|
|
75
108
|
end
|
|
76
109
|
|
|
110
|
+
# @rbs (Integer? max_length) -> Integer?
|
|
77
111
|
def validate_max_length(max_length)
|
|
78
112
|
return max_length if valid_integer_option?(max_length) || max_length.nil?
|
|
79
113
|
raise InvalidConfigurationError, "Max length must be an integer greater than 0"
|
|
80
114
|
end
|
|
81
115
|
|
|
116
|
+
# @rbs (Integer max_inputs_per_id) -> Integer
|
|
82
117
|
def validate_max_input(max_inputs_per_id)
|
|
83
118
|
return max_inputs_per_id if valid_integer_option?(max_inputs_per_id)
|
|
84
119
|
raise InvalidConfigurationError, "Max inputs per ID must be an integer greater than 0"
|
|
85
120
|
end
|
|
86
121
|
|
|
87
122
|
# Split the encoded string into groups of this size
|
|
123
|
+
# @rbs (Integer? split_at) -> Integer?
|
|
88
124
|
def validate_split_at(split_at)
|
|
89
125
|
return split_at if valid_integer_option?(split_at) || split_at.nil?
|
|
90
126
|
raise InvalidConfigurationError, "Split at must be an integer greater than 0 or nil"
|
|
91
127
|
end
|
|
92
128
|
|
|
129
|
+
# @rbs (String? split_with, Alphabet alphabet) -> String?
|
|
93
130
|
def validate_split_with(split_with, alphabet)
|
|
94
131
|
return split_with if split_with.nil? || (split_with.is_a?(String) && !alphabet.characters.include?(split_with))
|
|
95
132
|
raise InvalidConfigurationError, "Split with must be a string and not part of the alphabet or nil"
|
|
96
133
|
end
|
|
97
134
|
|
|
135
|
+
# @rbs (Integer? value) -> bool
|
|
98
136
|
def valid_integer_option?(value)
|
|
99
137
|
value.is_a?(Integer) && value > 0
|
|
100
138
|
end
|
|
101
139
|
|
|
140
|
+
# @rbs (encodeableValue value) -> Array[Integer]
|
|
102
141
|
def prepare_input(value)
|
|
103
142
|
inputs = value.is_a?(Array) ? value.map(&:to_i) : [value.to_i]
|
|
143
|
+
raise ::EncodedId::InvalidInputError, "Cannot encode an empty array" if inputs.empty?
|
|
104
144
|
raise ::EncodedId::InvalidInputError, "Integer IDs to be encoded can only be positive" if inputs.any?(&:negative?)
|
|
105
145
|
|
|
106
146
|
raise ::EncodedId::InvalidInputError, "%d integer IDs provided, maximum amount of IDs is %d" % [inputs.length, @max_inputs_per_id] if inputs.length > @max_inputs_per_id
|
|
@@ -108,24 +148,69 @@ module EncodedId
|
|
|
108
148
|
inputs
|
|
109
149
|
end
|
|
110
150
|
|
|
111
|
-
|
|
112
|
-
|
|
151
|
+
# @rbs (Symbol | Encoders::Base encoder) -> Encoders::Base
|
|
152
|
+
def create_encoder(encoder)
|
|
153
|
+
# If an encoder instance was provided, return it directly
|
|
154
|
+
return @encoder if defined?(@encoder) && @encoder.is_a?(Encoders::Base)
|
|
155
|
+
return encoder if encoder.is_a?(Encoders::Base)
|
|
156
|
+
|
|
157
|
+
case encoder
|
|
158
|
+
when :sqids
|
|
159
|
+
if defined?(Encoders::Sqids)
|
|
160
|
+
Encoders::Sqids.new(salt, length, alphabet, @blocklist)
|
|
161
|
+
else
|
|
162
|
+
raise InvalidConfigurationError, "Sqids encoder requested but the sqids gem is not available. Please add 'gem \"sqids\"' to your Gemfile."
|
|
163
|
+
end
|
|
164
|
+
when :hashids
|
|
165
|
+
Encoders::HashId.new(salt, length, alphabet, @blocklist)
|
|
166
|
+
else
|
|
167
|
+
raise InvalidConfigurationError, "The encoder name is not supported '#{encoder}'"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# @rbs (Symbol | Encoders::Base encoder) -> (Symbol | Encoders::Base)
|
|
172
|
+
def validate_encoder(encoder)
|
|
173
|
+
# Accept either a valid symbol or an Encoders::Base instance
|
|
174
|
+
return encoder if VALID_ENCODERS.include?(encoder) || encoder.is_a?(Encoders::Base)
|
|
175
|
+
raise InvalidConfigurationError, "Encoder must be one of: #{VALID_ENCODERS.join(", ")} or an instance of EncodedId::Encoders::Base"
|
|
113
176
|
end
|
|
114
177
|
|
|
115
|
-
|
|
116
|
-
|
|
178
|
+
# @rbs (Blocklist | Array[String] | Set[String] | nil blocklist) -> Blocklist
|
|
179
|
+
def validate_blocklist(blocklist)
|
|
180
|
+
return blocklist if blocklist.is_a?(Blocklist)
|
|
181
|
+
return Blocklist.empty if blocklist.nil?
|
|
182
|
+
|
|
183
|
+
return Blocklist.new(blocklist) if blocklist.is_a?(Array) || blocklist.is_a?(Set)
|
|
184
|
+
|
|
185
|
+
raise InvalidConfigurationError, "Blocklist must be an instance of Blocklist, a Set, or an Array of strings"
|
|
117
186
|
end
|
|
118
187
|
|
|
188
|
+
# @rbs (String hash) -> String
|
|
119
189
|
def humanize_length(hash)
|
|
120
|
-
hash.
|
|
190
|
+
len = hash.length
|
|
191
|
+
at = split_at #: Integer
|
|
192
|
+
with = split_with #: String
|
|
193
|
+
return hash if len <= at
|
|
194
|
+
|
|
195
|
+
separator_count = (len - 1) / at
|
|
196
|
+
result = hash.dup
|
|
197
|
+
insert_offset = 0
|
|
198
|
+
(1..separator_count).each do |i|
|
|
199
|
+
insert_pos = i * at + insert_offset
|
|
200
|
+
result.insert(insert_pos, with)
|
|
201
|
+
insert_offset += with.length
|
|
202
|
+
end
|
|
203
|
+
result
|
|
121
204
|
end
|
|
122
205
|
|
|
206
|
+
# @rbs (String str, bool downcase) -> String
|
|
123
207
|
def convert_to_hash(str, downcase)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
map_equivalent_characters(
|
|
208
|
+
str = str.gsub(split_with, "") if split_with
|
|
209
|
+
str = str.downcase if downcase
|
|
210
|
+
map_equivalent_characters(str)
|
|
127
211
|
end
|
|
128
212
|
|
|
213
|
+
# @rbs (String str) -> String
|
|
129
214
|
def map_equivalent_characters(str)
|
|
130
215
|
return str unless alphabet.equivalences
|
|
131
216
|
|
|
@@ -135,6 +220,7 @@ module EncodedId
|
|
|
135
220
|
end
|
|
136
221
|
end
|
|
137
222
|
|
|
223
|
+
# @rbs (String str) -> bool
|
|
138
224
|
def max_length_exceeded?(str)
|
|
139
225
|
return false if max_length.nil?
|
|
140
226
|
|
data/lib/encoded_id/version.rb
CHANGED
data/lib/encoded_id.rb
CHANGED
|
@@ -1,26 +1,59 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
3
5
|
require_relative "encoded_id/version"
|
|
4
6
|
require_relative "encoded_id/alphabet"
|
|
5
7
|
require_relative "encoded_id/hex_representation"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
require_relative "encoded_id/
|
|
10
|
-
require_relative "encoded_id/
|
|
8
|
+
require_relative "encoded_id/blocklist"
|
|
9
|
+
|
|
10
|
+
# Load the encoder framework
|
|
11
|
+
require_relative "encoded_id/encoders/base"
|
|
12
|
+
require_relative "encoded_id/encoders/hash_id_salt"
|
|
13
|
+
require_relative "encoded_id/encoders/hash_id_consistent_shuffle"
|
|
14
|
+
require_relative "encoded_id/encoders/hash_id_ordinal_alphabet_separator_guards"
|
|
15
|
+
require_relative "encoded_id/encoders/hash_id"
|
|
16
|
+
|
|
17
|
+
# Only load Sqids encoder if the gem is available
|
|
18
|
+
begin
|
|
19
|
+
require "sqids"
|
|
20
|
+
require_relative "encoded_id/encoders/my_sqids"
|
|
21
|
+
require_relative "encoded_id/encoders/sqids"
|
|
22
|
+
rescue LoadError
|
|
23
|
+
# Sqids gem not available, encoder will not be loaded
|
|
24
|
+
end
|
|
11
25
|
|
|
12
26
|
require_relative "encoded_id/reversible_id"
|
|
13
27
|
|
|
28
|
+
# @rbs!
|
|
29
|
+
# class Integer
|
|
30
|
+
# MAX: Integer
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# # Optional Sqids gem support
|
|
34
|
+
# module Sqids
|
|
35
|
+
# DEFAULT_BLOCKLIST: Array[String]
|
|
36
|
+
# end
|
|
37
|
+
|
|
14
38
|
module EncodedId
|
|
39
|
+
# @rbs InvalidConfigurationError: singleton(StandardError)
|
|
15
40
|
class InvalidConfigurationError < StandardError; end
|
|
16
41
|
|
|
42
|
+
# @rbs InvalidAlphabetError: singleton(ArgumentError)
|
|
17
43
|
class InvalidAlphabetError < ArgumentError; end
|
|
18
44
|
|
|
45
|
+
# @rbs EncodedIdFormatError: singleton(ArgumentError)
|
|
19
46
|
class EncodedIdFormatError < ArgumentError; end
|
|
20
47
|
|
|
48
|
+
# @rbs EncodedIdLengthError: singleton(ArgumentError)
|
|
21
49
|
class EncodedIdLengthError < ArgumentError; end
|
|
22
50
|
|
|
51
|
+
# @rbs InvalidInputError: singleton(ArgumentError)
|
|
23
52
|
class InvalidInputError < ArgumentError; end
|
|
24
53
|
|
|
54
|
+
# @rbs BlocklistError: singleton(StandardError)
|
|
55
|
+
class BlocklistError < StandardError; end
|
|
56
|
+
|
|
57
|
+
# @rbs SaltError: singleton(ArgumentError)
|
|
25
58
|
class SaltError < ArgumentError; end
|
|
26
59
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: encoded_id
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.0.
|
|
4
|
+
version: 1.0.0.rc6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stephen Ierodiaconou
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2025-11-17 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: Encode your numerical IDs (eg record primary keys) into obfuscated strings
|
|
13
13
|
that can be used in URLs. The obfuscated strings are reversible, so you can decode
|
|
@@ -20,54 +20,45 @@ executables: []
|
|
|
20
20
|
extensions: []
|
|
21
21
|
extra_rdoc_files: []
|
|
22
22
|
files:
|
|
23
|
-
- ".devcontainer/Dockerfile"
|
|
24
|
-
- ".devcontainer/compose.yml"
|
|
25
|
-
- ".devcontainer/devcontainer.json"
|
|
26
|
-
- ".standard.yml"
|
|
27
23
|
- CHANGELOG.md
|
|
28
|
-
- Gemfile
|
|
29
24
|
- LICENSE.txt
|
|
30
25
|
- README.md
|
|
31
|
-
-
|
|
32
|
-
- Steepfile
|
|
33
|
-
- ext/encoded_id/extconf.rb
|
|
34
|
-
- ext/encoded_id/extension.c
|
|
35
|
-
- ext/encoded_id/hashids.c
|
|
36
|
-
- ext/encoded_id/hashids.h
|
|
26
|
+
- context/encoded_id.md
|
|
37
27
|
- lib/encoded_id.rb
|
|
38
28
|
- lib/encoded_id/alphabet.rb
|
|
39
|
-
- lib/encoded_id/
|
|
40
|
-
- lib/encoded_id/
|
|
41
|
-
- lib/encoded_id/
|
|
29
|
+
- lib/encoded_id/blocklist.rb
|
|
30
|
+
- lib/encoded_id/encoders/base.rb
|
|
31
|
+
- lib/encoded_id/encoders/hash_id.rb
|
|
32
|
+
- lib/encoded_id/encoders/hash_id_consistent_shuffle.rb
|
|
33
|
+
- lib/encoded_id/encoders/hash_id_ordinal_alphabet_separator_guards.rb
|
|
34
|
+
- lib/encoded_id/encoders/hash_id_salt.rb
|
|
35
|
+
- lib/encoded_id/encoders/my_sqids.rb
|
|
36
|
+
- lib/encoded_id/encoders/sqids.rb
|
|
42
37
|
- lib/encoded_id/hex_representation.rb
|
|
43
|
-
- lib/encoded_id/ordinal_alphabet_separator_guards.rb
|
|
44
38
|
- lib/encoded_id/reversible_id.rb
|
|
45
39
|
- lib/encoded_id/version.rb
|
|
46
|
-
- rbs_collection.yaml
|
|
47
|
-
- sig/encoded_id.rbs
|
|
48
40
|
homepage: https://github.com/stevegeek/encoded_id
|
|
49
41
|
licenses:
|
|
50
42
|
- MIT
|
|
51
43
|
metadata:
|
|
52
44
|
homepage_uri: https://github.com/stevegeek/encoded_id
|
|
53
45
|
source_code_uri: https://github.com/stevegeek/encoded_id
|
|
54
|
-
changelog_uri: https://github.com/stevegeek/encoded_id/blob/
|
|
46
|
+
changelog_uri: https://github.com/stevegeek/encoded_id/blob/main/CHANGELOG.md
|
|
55
47
|
rdoc_options: []
|
|
56
48
|
require_paths:
|
|
57
49
|
- lib
|
|
58
|
-
- ext
|
|
59
50
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
51
|
requirements:
|
|
61
52
|
- - ">="
|
|
62
53
|
- !ruby/object:Gem::Version
|
|
63
|
-
version: 2.
|
|
54
|
+
version: 3.2.0
|
|
64
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
56
|
requirements:
|
|
66
57
|
- - ">="
|
|
67
58
|
- !ruby/object:Gem::Version
|
|
68
59
|
version: '0'
|
|
69
60
|
requirements: []
|
|
70
|
-
rubygems_version: 3.6.
|
|
61
|
+
rubygems_version: 3.6.2
|
|
71
62
|
specification_version: 4
|
|
72
63
|
summary: EncodedId is a gem for creating reversible obfuscated IDs from numerical
|
|
73
64
|
IDs. It uses an implementation of Hash IDs under the hood.
|
data/.devcontainer/Dockerfile
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version or gemspec
|
|
2
|
-
ARG RUBY_VERSION=3.4.2
|
|
3
|
-
FROM ghcr.io/rails/devcontainer/images/ruby:$RUBY_VERSION
|
|
4
|
-
|
|
5
|
-
USER vscode
|
|
6
|
-
|
|
7
|
-
# Ensure binding is always 0.0.0.0
|
|
8
|
-
# Binds the server to all IP addresses of the container, so it can be accessed from outside the container.
|
|
9
|
-
ENV BINDING="0.0.0.0"
|
data/.devcontainer/compose.yml
DELETED
data/.standard.yml
DELETED
data/Gemfile
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
# Specify your gem's dependencies in encoded_id.gemspec
|
|
6
|
-
gemspec
|
|
7
|
-
|
|
8
|
-
gem "rake"
|
|
9
|
-
|
|
10
|
-
gem "minitest"
|
|
11
|
-
|
|
12
|
-
gem "standard"
|
|
13
|
-
|
|
14
|
-
# gem "rbs"
|
|
15
|
-
#
|
|
16
|
-
# gem "steep"
|
|
17
|
-
|
|
18
|
-
gem "simplecov"
|
|
19
|
-
|
|
20
|
-
gem "benchmark-ips"
|
|
21
|
-
|
|
22
|
-
gem "benchmark-memory"
|
|
23
|
-
|
|
24
|
-
gem "fuzzbert"
|
|
25
|
-
|
|
26
|
-
gem "singed"
|
|
27
|
-
|
|
28
|
-
gem "memory_profiler"
|
|
29
|
-
|
|
30
|
-
gem "hashids" # For benchmarking against
|
|
31
|
-
|
|
32
|
-
gem "base64"
|
|
33
|
-
|
|
34
|
-
# gem "pf2", require: false, github: "osyoyu/pf2", branch: "main"
|
|
35
|
-
|
|
36
|
-
# gem "vernier", require: false
|
data/Rakefile
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "bundler/gem_tasks"
|
|
4
|
-
require "rake/testtask"
|
|
5
|
-
|
|
6
|
-
Rake::TestTask.new(:test) do |t|
|
|
7
|
-
t.libs << "test"
|
|
8
|
-
t.libs << "lib"
|
|
9
|
-
t.test_files = FileList["test/**/test_*.rb"]
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
task default: %i[test standard]
|
|
13
|
-
|
|
14
|
-
task :compile_ext do
|
|
15
|
-
puts "Compiling extension"
|
|
16
|
-
`cd ext/encoded_id && make clean`
|
|
17
|
-
`cd ext/encoded_id && ruby extconf.rb`
|
|
18
|
-
`cd ext/encoded_id && make`
|
|
19
|
-
puts "Done"
|
|
20
|
-
end
|
data/Steepfile
DELETED
data/ext/encoded_id/extconf.rb
DELETED
data/ext/encoded_id/extension.c
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
#include "ruby/ruby.h"
|
|
2
|
-
#include "hashids.h"
|
|
3
|
-
|
|
4
|
-
void wrapped_hashids_free(void* data)
|
|
5
|
-
{
|
|
6
|
-
hashids_free(data);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
size_t wrapped_hashids_size(const void* data)
|
|
10
|
-
{
|
|
11
|
-
return sizeof(hashids_t);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
static const rb_data_type_t wrapped_hashids_type = {
|
|
15
|
-
.wrap_struct_name = "hashids_t",
|
|
16
|
-
.function = {
|
|
17
|
-
.dmark = NULL,
|
|
18
|
-
.dfree = wrapped_hashids_free,
|
|
19
|
-
.dsize = wrapped_hashids_size,
|
|
20
|
-
},
|
|
21
|
-
.data = NULL,
|
|
22
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
VALUE hashids_alloc(VALUE self)
|
|
26
|
-
{
|
|
27
|
-
hashids_t *data = hashids_init("salt!");
|
|
28
|
-
return TypedData_Wrap_Struct(self, &wrapped_hashids_type, data);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
//VALUE rb_hashids_m_initialize(VALUE self, VALUE val)
|
|
32
|
-
//{
|
|
33
|
-
// return self;
|
|
34
|
-
//}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
static VALUE rb_hash_id_c_encode(VALUE self, VALUE ids) {
|
|
38
|
-
Check_Type(ids, T_ARRAY);
|
|
39
|
-
|
|
40
|
-
long length = RARRAY_LEN(ids);
|
|
41
|
-
|
|
42
|
-
unsigned long long* inputs = ALLOC_N(unsigned long long, length);
|
|
43
|
-
|
|
44
|
-
for (long i = 0; i < length; i++) {
|
|
45
|
-
VALUE rb_element = rb_ary_entry(ids, i);
|
|
46
|
-
Check_Type(rb_element, T_FIXNUM);
|
|
47
|
-
inputs[i] = NUM2ULL(rb_element);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
hashids_t* hashids;
|
|
51
|
-
|
|
52
|
-
TypedData_Get_Struct(self, hashids_t, &wrapped_hashids_type, hashids);
|
|
53
|
-
|
|
54
|
-
size_t bytes_encoded;
|
|
55
|
-
|
|
56
|
-
size_t bytes_needed;
|
|
57
|
-
bytes_needed = hashids_estimate_encoded_size(hashids, sizeof(&inputs) / sizeof(unsigned long long), &inputs);
|
|
58
|
-
char *hash = ALLOC_N(char, bytes_needed);
|
|
59
|
-
|
|
60
|
-
// unsigned long long numbers[] = {1ull, 2ull, 3ull, 4ull, 5ull};
|
|
61
|
-
|
|
62
|
-
// printf("length: %ld\n", length);
|
|
63
|
-
// printf("inputs[0]: %llu\n", inputs[0]);
|
|
64
|
-
// printf("inputs[1]: %llu\n", inputs[1]);
|
|
65
|
-
// printf("inputs[2]: %llu\n", inputs[2]);
|
|
66
|
-
// printf("inputs[3]: %llu\n", inputs[3]);
|
|
67
|
-
// printf("inputs[4]: %llu\n", inputs[4]);
|
|
68
|
-
//
|
|
69
|
-
// printf("hashids: %p\n", hashids);
|
|
70
|
-
// printf("hashids->alphabet: %s\n", hashids->alphabet);
|
|
71
|
-
// printf("hashids->salt: %s\n", hashids->salt);
|
|
72
|
-
// printf("hashids->min_hash_length: %lu\n", hashids->min_hash_length);
|
|
73
|
-
// printf("numbers: %p\n", numbers);
|
|
74
|
-
// printf("numbers[0]: %llu\n", numbers[0]);
|
|
75
|
-
// printf("numbers[1]: %llu\n", numbers[1]);
|
|
76
|
-
// printf("numbers[2]: %llu\n", numbers[2]);
|
|
77
|
-
// printf("numbers[3]: %llu\n", numbers[3]);
|
|
78
|
-
// printf("numbers[4]: %llu\n", numbers[4]);
|
|
79
|
-
//
|
|
80
|
-
// printf("sizeof(*inputs) / sizeof(unsigned long long): %lu\n", sizeof(*inputs) / sizeof(unsigned long long));
|
|
81
|
-
// printf("sizeof(numbers) / sizeof(unsigned long long): %lu\n", sizeof(numbers) / sizeof(unsigned long long));
|
|
82
|
-
// bytes_encoded = hashids_encode(hashids, hash, sizeof(numbers) / sizeof(unsigned long long), numbers);
|
|
83
|
-
bytes_encoded = hashids_encode(hashids, hash, length, inputs);
|
|
84
|
-
|
|
85
|
-
ruby_xfree(inputs);
|
|
86
|
-
VALUE return_value = rb_str_new2(hash);
|
|
87
|
-
ruby_xfree(hash);
|
|
88
|
-
return return_value;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
static VALUE rb_hash_id_c_decode(VALUE self, VALUE str) {
|
|
92
|
-
Check_Type(str, T_STRING);
|
|
93
|
-
|
|
94
|
-
hashids_t* hashids;
|
|
95
|
-
|
|
96
|
-
TypedData_Get_Struct(self, hashids_t, &wrapped_hashids_type, hashids);
|
|
97
|
-
|
|
98
|
-
size_t numbers_count = hashids_numbers_count(hashids, RSTRING_PTR(str));
|
|
99
|
-
|
|
100
|
-
unsigned long long* numbers = ALLOC_N(unsigned long long, numbers_count);
|
|
101
|
-
|
|
102
|
-
hashids_decode_safe(hashids, RSTRING_PTR(str), numbers, numbers_count);
|
|
103
|
-
|
|
104
|
-
VALUE rb_numbers = rb_ary_new_capa(numbers_count);
|
|
105
|
-
|
|
106
|
-
for (size_t i = 0; i < numbers_count; i++) {
|
|
107
|
-
rb_ary_push(rb_numbers, ULL2NUM(numbers[i]));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
ruby_xfree(numbers);
|
|
111
|
-
return rb_numbers;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
void Init_extension(void) {
|
|
115
|
-
VALUE EncodedId = rb_define_module("EncodedId");
|
|
116
|
-
VALUE HashIdC = rb_define_class_under(EncodedId, "HashIdC", rb_cObject);
|
|
117
|
-
|
|
118
|
-
rb_define_alloc_func(HashIdC, hashids_alloc);
|
|
119
|
-
// rb_define_method(HashIdC, "initialize", rb_hashids_m_initialize, 1);
|
|
120
|
-
|
|
121
|
-
rb_define_method(HashIdC, "encode", rb_hash_id_c_encode, 1);
|
|
122
|
-
rb_define_method(HashIdC, "decode", rb_hash_id_c_decode, 1);
|
|
123
|
-
}
|