cool_id 0.1.7 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd52ffe11cf7b25bf473b06e879a7df67eb4aea3b9169b0961342fef30a6fce7
4
- data.tar.gz: b92c14dd88880ad52de1884f8a1e2720915b5ee24b742c0db8a966071bd07196
3
+ metadata.gz: 07011e094fb16385d448b80b4461b0e579d29efc0b4273fff4b41dd1df350a64
4
+ data.tar.gz: 11d8a83f95a7fce11598df98d1b7175c26b00f8a0d19d6af18ebaa35b8ddfa0b
5
5
  SHA512:
6
- metadata.gz: f41bf3c022796fdaf4e8f53af035b6515c6a6b5ac59b749c52a17247bb6710b7296424d2a86c20f484500cae15f7daec32ec1a3980717441a0fec186ad528945
7
- data.tar.gz: c81557d5b123b122be77208ef6c74421d81c048dd409f289421ee1bfbaea075550621c77d0c6d92f5b11df393b4fd3be820e62cf2ef491a8e48958705e218d5c
6
+ metadata.gz: f98ab9a494119599b383cc41bc9fdb797f68e793f626b07759899fee06ded282b6b8fe4f98d8ab82425127c64f4daac2511b8645f61458625e8356c8a428efe0
7
+ data.tar.gz: 6002ff50f083a330ad2fcdb582df9e12d9aea31d26e1d7f737dcd20650c0fa7454e2eb43a7f88c84e47bcdb5f9965557433d97acfca13b0afa854032c4de28fa
data/.aider.conf.yml ADDED
@@ -0,0 +1,9 @@
1
+ # https://aider.chat/docs/config/aider_conf.html
2
+
3
+ lint-cmd: bundle exec standardrb --fix
4
+ auto-lint: true
5
+
6
+ test-cmd: bundle exec rspec --format progress --no-profile --no-color --fail-fast
7
+ auto-test: true
8
+
9
+ read: cool_id.gemspec
data/README.md CHANGED
@@ -35,9 +35,26 @@ and generate ids without creating a record
35
35
  # generate an id, e.g. for batch inserts or upserts
36
36
  User.generate_cool_id
37
37
  # => "usr_vktd1b5v84lr"
38
+ ```
39
+
40
+ you can use cool_id with a separate field, keeping the default primary key:
41
+
42
+ ```ruby
43
+ class Product < ActiveRecord::Base
44
+ include CoolId::Model
45
+ cool_id prefix: "prd", id_field: :public_id
46
+ end
38
47
 
48
+ product = Product.create!(name: "Cool Product")
49
+ product.id # => 1 (or another integer)
50
+ product.public_id # => "prd_vktd1b5v84lr"
51
+
52
+ # You can still use CoolId.locate with the public_id
53
+ CoolId.locate("prd_vktd1b5v84lr") # => #<Product id: 1, public_id: "prd_vktd1b5v84lr", name: "Cool Product">
39
54
  ```
40
55
 
56
+ this approach allows you to keep your primary key as an auto-incrementing integer while still benefiting from CoolId's functionality. it's particularly useful when you want to expose a public identifier that's separate from your internal primary key.
57
+
41
58
  it takes parameters to change the alphabet or length
42
59
 
43
60
  ```ruby
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CoolId
4
- VERSION = "0.1.7"
4
+ VERSION = "0.1.9"
5
5
  end
data/lib/cool_id.rb CHANGED
@@ -7,15 +7,18 @@ require "active_support/concern"
7
7
  module CoolId
8
8
  class NotConfiguredError < StandardError; end
9
9
 
10
+ class MaxRetriesExceededError < StandardError; end
11
+
10
12
  # defaults based on https://planetscale.com/blog/why-we-chose-nanoids-for-planetscales-api
11
13
  DEFAULT_SEPARATOR = "_"
12
14
  DEFAULT_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"
13
15
  DEFAULT_LENGTH = 12
16
+ DEFAULT_MAX_RETRIES = 1000
14
17
 
15
- Id = Struct.new(:key, :prefix, :id, :model_class)
18
+ Id = Struct.new(:key, :prefix, :id, :model_class, :id_field)
16
19
 
17
20
  class << self
18
- attr_accessor :separator, :alphabet, :length
21
+ attr_accessor :separator, :alphabet, :length, :max_retries, :id_field
19
22
 
20
23
  def configure
21
24
  yield self
@@ -25,6 +28,8 @@ module CoolId
25
28
  self.separator = DEFAULT_SEPARATOR
26
29
  self.alphabet = DEFAULT_ALPHABET
27
30
  self.length = DEFAULT_LENGTH
31
+ self.max_retries = DEFAULT_MAX_RETRIES
32
+ self.id_field = nil
28
33
  end
29
34
 
30
35
  def registry
@@ -34,15 +39,32 @@ module CoolId
34
39
  def generate_id(config)
35
40
  alphabet = config.alphabet || @alphabet
36
41
  length = config.length || @length
37
- id = Nanoid.generate(size: length, alphabet: alphabet)
42
+ max_retries = config.max_retries || @max_retries
43
+
44
+ retries = 0
45
+ loop do
46
+ nano_id = Nanoid.generate(size: length, alphabet: alphabet)
47
+ full_id = "#{config.prefix}#{separator}#{nano_id}"
48
+ if !config.model_class.exists?(id: full_id)
49
+ return full_id
50
+ end
38
51
 
39
- "#{config.prefix}#{separator}#{id}"
52
+ retries += 1
53
+ if retries >= max_retries
54
+ raise MaxRetriesExceededError, "Failed to generate a unique ID after #{max_retries} attempts"
55
+ end
56
+ end
57
+ end
58
+
59
+ def resolve_cool_id_field(model_class)
60
+ model_class.cool_id_config&.id_field || CoolId.id_field || model_class.primary_key
40
61
  end
41
62
  end
42
63
 
43
64
  self.separator = DEFAULT_SEPARATOR
44
65
  self.alphabet = DEFAULT_ALPHABET
45
66
  self.length = DEFAULT_LENGTH
67
+ self.max_retries = DEFAULT_MAX_RETRIES
46
68
 
47
69
  class Registry
48
70
  def initialize
@@ -55,24 +77,31 @@ module CoolId
55
77
 
56
78
  def locate(id)
57
79
  parsed = parse(id)
58
- parsed&.model_class&.find_by(id: id)
80
+ return nil unless parsed
81
+
82
+ id_field = CoolId.resolve_cool_id_field(parsed.model_class)
83
+ parsed.model_class.find_by(id_field => id)
59
84
  end
60
85
 
61
86
  def parse(id)
62
87
  prefix, key = id.split(CoolId.separator, 2)
63
88
  model_class = @prefix_map[prefix]
64
89
  return nil unless model_class
65
- Id.new(key, prefix, id, model_class)
90
+ id_field = CoolId.resolve_cool_id_field(model_class)
91
+ Id.new(key, prefix, id, model_class, id_field)
66
92
  end
67
93
  end
68
94
 
69
95
  class Config
70
- attr_reader :prefix, :length, :alphabet
96
+ attr_reader :prefix, :length, :alphabet, :max_retries, :model_class, :id_field
71
97
 
72
- def initialize(prefix:, length: nil, alphabet: nil)
98
+ def initialize(prefix:, model_class:, length: nil, alphabet: nil, max_retries: nil, id_field: nil)
73
99
  @length = length
74
100
  @prefix = validate_prefix(prefix)
75
101
  @alphabet = validate_alphabet(alphabet)
102
+ @max_retries = max_retries
103
+ @model_class = model_class
104
+ @id_field = id_field
76
105
  end
77
106
 
78
107
  private
@@ -98,7 +127,7 @@ module CoolId
98
127
  attr_accessor :cool_id_setup_required
99
128
 
100
129
  def cool_id(options)
101
- @cool_id_config = Config.new(**options)
130
+ @cool_id_config = Config.new(**options, model_class: self)
102
131
  CoolId.registry.register(options[:prefix], self)
103
132
  end
104
133
 
@@ -129,7 +158,8 @@ module CoolId
129
158
  private
130
159
 
131
160
  def set_cool_id
132
- self.id = self.class.generate_cool_id if id.blank?
161
+ id_field = CoolId.resolve_cool_id_field(self.class)
162
+ self[id_field] = self.class.generate_cool_id if self[id_field].blank?
133
163
  end
134
164
 
135
165
  def ensure_cool_id_configured
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cool_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Schilling
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-20 00:00:00.000000000 Z
11
+ date: 2024-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nanoid
@@ -73,6 +73,7 @@ executables: []
73
73
  extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
+ - ".aider.conf.yml"
76
77
  - ".rspec"
77
78
  - ".standard.yml"
78
79
  - CHANGELOG.md