neat_ids 1.0.1 → 1.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/Rakefile +0 -2
- data/lib/neat_ids/neat_id.rb +25 -6
- data/lib/neat_ids/version.rb +1 -1
- data/lib/neat_ids.rb +66 -2
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 71d2b22c9ed3296f5b52c0fcb193caee551837cb351c5c7200e985da420df33c
|
|
4
|
+
data.tar.gz: e0df512bd2bd22df8f44d0a83acc863a37bb9d3affa3ab05bb5806087b6718cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 23715fa9a6cd461ed7970f775903632ba35afa785682499029b21b0c3ca0cbdecdcfb1e7caecb848a87eefd601af8df308a9d10be527ca31ecb690fc58249a34
|
|
7
|
+
data.tar.gz: f658ad44a57b92be00d2c76c822839874f362c07fff00ef7615e943c63fbcc6104209aa64b8bc0f26460d0e76cf55dc79aef93eb1bca3adcd9eb8557f2a49a8f
|
data/Rakefile
CHANGED
data/lib/neat_ids/neat_id.rb
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
module NeatIds
|
|
2
2
|
class NeatId
|
|
3
|
-
attr_reader :sqids, :prefix
|
|
3
|
+
attr_reader :sqids, :prefix, :salt
|
|
4
4
|
|
|
5
5
|
TOKEN = 123
|
|
6
6
|
|
|
7
|
-
def initialize(model, prefix, min_length: NeatIds.minimum_length, alphabet: NeatIds.alphabet, delimiter: NeatIds.delimiter, **options)
|
|
7
|
+
def initialize(model, prefix, min_length: NeatIds.minimum_length, alphabet: NeatIds.alphabet, delimiter: NeatIds.delimiter, salt: NeatIds.salt, **options)
|
|
8
8
|
@prefix = prefix.to_s
|
|
9
9
|
@delimiter = delimiter.to_s
|
|
10
|
-
@
|
|
10
|
+
@salt = "#{model.table_name}#{salt}"
|
|
11
|
+
@sqids = Sqids.new(min_length: min_length, alphabet: shuffle_alphabet(alphabet))
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def encode(id)
|
|
@@ -38,6 +39,24 @@ module NeatIds
|
|
|
38
39
|
|
|
39
40
|
private
|
|
40
41
|
|
|
42
|
+
def shuffle_alphabet(alphabet)
|
|
43
|
+
chars = alphabet.chars
|
|
44
|
+
i = 0
|
|
45
|
+
p = 0
|
|
46
|
+
len = chars.length
|
|
47
|
+
|
|
48
|
+
salt_length = @salt.length
|
|
49
|
+
while i < len
|
|
50
|
+
v = salt_length.zero? ? 0 : @salt.getbyte(i % salt_length)
|
|
51
|
+
p += v
|
|
52
|
+
j = (v + p + i) % len
|
|
53
|
+
chars[i], chars[j] = chars[j], chars[i]
|
|
54
|
+
i += 1
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
chars.join
|
|
58
|
+
end
|
|
59
|
+
|
|
41
60
|
def valid?(decoded_hashid)
|
|
42
61
|
decoded_hashid.size >= 2 && decoded_hashid.first == TOKEN
|
|
43
62
|
end
|
|
@@ -47,13 +66,13 @@ module NeatIds
|
|
|
47
66
|
end
|
|
48
67
|
|
|
49
68
|
def uuid_to_ints(uuid)
|
|
50
|
-
hex = uuid.delete(
|
|
69
|
+
hex = uuid.delete("-")
|
|
51
70
|
[0, 8, 16, 24].map { |i| hex[i, 8].to_i(16) }
|
|
52
71
|
end
|
|
53
72
|
|
|
54
73
|
def ints_to_uuid(ints)
|
|
55
|
-
hex = ints.map { |n| n.to_s(16).rjust(8,
|
|
56
|
-
"#{hex[0,8]}-#{hex[8,4]}-#{hex[12,4]}-#{hex[16,4]}-#{hex[20,12]}"
|
|
74
|
+
hex = ints.map { |n| n.to_s(16).rjust(8, "0") }.join
|
|
75
|
+
"#{hex[0, 8]}-#{hex[8, 4]}-#{hex[12, 4]}-#{hex[16, 4]}-#{hex[20, 12]}"
|
|
57
76
|
end
|
|
58
77
|
end
|
|
59
78
|
end
|
data/lib/neat_ids/version.rb
CHANGED
data/lib/neat_ids.rb
CHANGED
|
@@ -3,6 +3,19 @@ require "neat_ids/engine"
|
|
|
3
3
|
|
|
4
4
|
require "sqids"
|
|
5
5
|
|
|
6
|
+
# The upstream sqids gem derives its max encodable number from `0.size`
|
|
7
|
+
# (the machine word size). On 32-bit platforms (e.g. arm32) that yields
|
|
8
|
+
# 2**30 - 1 = 1073741823, which is too small for the 32-bit integer chunks
|
|
9
|
+
# used to encode UUIDs and breaks encoding on machines like the Raspberry Pi 2.
|
|
10
|
+
# `max_value` only acts as an upper-bound guard in `Sqids#encode`; it does not
|
|
11
|
+
# affect the encoding/decoding algorithm, so pinning it to a fixed constant is
|
|
12
|
+
# safe and produces identical IDs on every platform.
|
|
13
|
+
class Sqids
|
|
14
|
+
def self.max_value
|
|
15
|
+
9007199254740991 # 2**53 - 1, per the Sqids spec
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
6
19
|
module NeatIds
|
|
7
20
|
class Error < StandardError; end
|
|
8
21
|
|
|
@@ -11,6 +24,7 @@ module NeatIds
|
|
|
11
24
|
mattr_accessor :delimiter, default: "_"
|
|
12
25
|
mattr_accessor :alphabet, default: "abcdefghijklmnopqrstuvwxyz1234567890"
|
|
13
26
|
mattr_accessor :minimum_length, default: 24
|
|
27
|
+
mattr_accessor :salt, default: ""
|
|
14
28
|
|
|
15
29
|
mattr_accessor :models, default: {}
|
|
16
30
|
|
|
@@ -21,6 +35,16 @@ module NeatIds
|
|
|
21
35
|
raise Error, "Unable to find model with prefix `#{prefix}`. Available prefixes are: #{models.keys.join(", ")}"
|
|
22
36
|
end
|
|
23
37
|
|
|
38
|
+
# Returns a decoded ID from a neat_id
|
|
39
|
+
def self.decode_neat_id(neat_id)
|
|
40
|
+
return neat_id if neat_id.nil? || neat_id.is_a?(Integer)
|
|
41
|
+
return neat_id unless models.keys.any? { |key| neat_id.to_s.start_with?("#{key}#{delimiter}") }
|
|
42
|
+
|
|
43
|
+
model_name, _ = split_id(neat_id)
|
|
44
|
+
|
|
45
|
+
models.fetch(model_name).decode_neat_id(neat_id)
|
|
46
|
+
end
|
|
47
|
+
|
|
24
48
|
# Splits a prefixed ID into its prefix and ID
|
|
25
49
|
def self.split_id(neat_id, delimiter = NeatIds.delimiter)
|
|
26
50
|
prefix, _, id = neat_id.to_s.rpartition(delimiter)
|
|
@@ -62,6 +86,13 @@ module NeatIds
|
|
|
62
86
|
module Attribute
|
|
63
87
|
extend ActiveSupport::Concern
|
|
64
88
|
|
|
89
|
+
# Methods added to relations and has_many associations
|
|
90
|
+
module RelationMethods
|
|
91
|
+
def neat_ids
|
|
92
|
+
klass.neat_ids(pluck(:id))
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
65
96
|
class_methods do
|
|
66
97
|
def find_by_neat_id(id)
|
|
67
98
|
find_by(id: _neat_id.decode(id))
|
|
@@ -86,6 +117,16 @@ module NeatIds
|
|
|
86
117
|
def decode_neat_ids(ids)
|
|
87
118
|
ids.map { |id| decode_neat_id(id) }
|
|
88
119
|
end
|
|
120
|
+
|
|
121
|
+
def relation
|
|
122
|
+
super.tap { |r| r.extend RelationMethods }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def has_many(*args, &block)
|
|
126
|
+
options = args.extract_options!
|
|
127
|
+
options[:extend] = Array(options[:extend]).push(RelationMethods)
|
|
128
|
+
super(*args, **options, &block)
|
|
129
|
+
end
|
|
89
130
|
end
|
|
90
131
|
|
|
91
132
|
def neat_id
|
|
@@ -112,12 +153,35 @@ module NeatIds
|
|
|
112
153
|
end
|
|
113
154
|
|
|
114
155
|
def relation
|
|
115
|
-
super.tap { |r| r.extend ClassMethods }
|
|
156
|
+
super.tap { |r| r.extend ClassMethods, Attribute::RelationMethods }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def belongs_to(*args, **options, &block)
|
|
160
|
+
association = super
|
|
161
|
+
|
|
162
|
+
name = args.first
|
|
163
|
+
reflection = association[name] || association[name.to_s]
|
|
164
|
+
|
|
165
|
+
return association if reflection.polymorphic?
|
|
166
|
+
return association if reflection.klass._neat_id.blank?
|
|
167
|
+
|
|
168
|
+
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
169
|
+
def #{name}_neat_id
|
|
170
|
+
#{reflection.klass}._neat_id.encode(#{reflection.foreign_key})
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def #{name}_neat_id=(neat_id)
|
|
174
|
+
decoded_id = #{reflection.klass}._neat_id.decode(neat_id, fallback: #{reflection.klass}._neat_id_fallback)
|
|
175
|
+
send("#{reflection.foreign_key}=", decoded_id)
|
|
176
|
+
end
|
|
177
|
+
CODE
|
|
178
|
+
|
|
179
|
+
association
|
|
116
180
|
end
|
|
117
181
|
|
|
118
182
|
def has_many(*args, &block)
|
|
119
183
|
options = args.extract_options!
|
|
120
|
-
options[:extend] = Array(options[:extend]).push(ClassMethods)
|
|
184
|
+
options[:extend] = Array(options[:extend]).push(ClassMethods, Attribute::RelationMethods)
|
|
121
185
|
super(*args, **options, &block)
|
|
122
186
|
end
|
|
123
187
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: neat_ids
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brad Thompson
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -59,7 +58,6 @@ metadata:
|
|
|
59
58
|
homepage_uri: https://github.com/braddoeswebdev/neat_ids
|
|
60
59
|
source_code_uri: https://github.com/braddoeswebdev/neat_ids
|
|
61
60
|
changelog_uri: https://github.com/braddoeswebdev/neat_ids
|
|
62
|
-
post_install_message:
|
|
63
61
|
rdoc_options: []
|
|
64
62
|
require_paths:
|
|
65
63
|
- lib
|
|
@@ -74,8 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
74
72
|
- !ruby/object:Gem::Version
|
|
75
73
|
version: '0'
|
|
76
74
|
requirements: []
|
|
77
|
-
rubygems_version:
|
|
78
|
-
signing_key:
|
|
75
|
+
rubygems_version: 4.0.14
|
|
79
76
|
specification_version: 4
|
|
80
77
|
summary: Neat IDs generates IDs with friendly prefixes for your models
|
|
81
78
|
test_files: []
|