cool_id 0.1.7 → 0.1.9

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 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